def __init__(self, head=(0.0, 0.0), tail=(1.0, 1.0)): if isinstance(head, (list, tuple)): head = D2.Point(*head) if isinstance(tail, (list, tuple)): tail = D2.Point(*tail) self.head = head self.tail = tail
def __init__(self, pos=(0.0, 0.0), size=(1.0, 1.0)): if isinstance(pos, (list, tuple)): pos = D2.Point(*pos) if isinstance(size, (list, tuple)): size = D2.Point(*size) self.ref_point = pos self.size = size
def set_guides (self) : try : src_c = self.source_connector.side trg_c = self.target_connector.side except AttributeError : print \ ( "Connector missing for %s: source_connector = %s" ", target_connector = %s" % (self, self.source_connector, self.target_connector) ) raise dim = src_c.dim delta = self.delta guides = self.guides = [] g_offset = self.guide_offset add = guides.append self._points = None if src_c.is_opposite (trg_c) : if getattr (delta, src_c.other_dim) != 0 : p2_a = D2.Point (** {dim : 1}) p2_b = D2.Point (* reversed (p2_a)) off = src_c.guide_offset (g_offset) add ((D2.Point (1, 1), D2.Point (0, 0), off)) add ((p2_a, p2_b, off)) elif src_c.side == trg_c.side : if getattr (delta, dim) == 0 : off = src_c.guide_offset (g_offset) add ((D2.Point (1, 1), D2.Point (0, 0), off)) add ((D2.Point (0, 0), D2.Point (1, 1), off)) else : p2_a = src_c.guide_point (0) p2_b = D2.Point (* reversed (p2_a)) add ((p2_a, p2_b))
def render_node(self, node, canvas): box = node.box pos = box.top_left + D2.Point(2, 1) width = box.size.x - 2 height = box.size.y - 2 def _label_parts(parts, width, height): i = 0 l = len(parts) x = "" while i < l and height > 0: p = x + parts[i] i += 1 while i < l and len(p) < width: if len(p) + len(parts[i]) < width: p += parts[i] i += 1 else: break yield p height -= 1 x = " " for lp in _label_parts(node.entity.label_parts, width, height): canvas.text(pos, lp) pos.shift((0, 1)) canvas.rectangle(box)
def _render_node_labels (self, node, grp) : P = self.Parameters box = node.box tp = box.ref_point + D2.Point (P.font_size // 2, 0) width = int ((box.size.x - P.font_size) / P.font_char_width) height = box.size.y / P.line_height - 1 def _label_parts (parts, width, height) : i = 0 l = len (parts) x = "" while i < l and height > 0: p = x + parts [i] i += 1 while i < l and len (p) < width : if len (p) + len (parts [i]) < width : p += parts [i] i += 1 else : break yield p height -= 1 txt = SVG.Text \ ( x = tp.x , y = tp.y , fill = P.color.text , font_family = P.font_family , font_size = P.font_size ) dx = 0 for lp in _label_parts (node.entity.label_parts, width, height) : txt.add (SVG.Tspan (lp, x = tp.x, dx = dx, dy = P.line_height)) dx = P.font_size // 2 grp.add (txt)
def point_in_rect(self, point): if isinstance(point, (list, tuple)): point = D2.Point(*point) tl = self.top_left br = tl + self.size if ((point.x < tl.x) or (point.x > br.x) or (point.y < tl.y) or (point.y > br.y)): return None return 1
def points_gen \ (self, head=None, tail=None, off_scale=0.5, y_inverted=False) : if head is None : head = self.source.pos if tail is None : tail = self.target.pos guides = self.guides zero = D2.Point (0, 0) y_corr = D2.Point (1, (1, -1) [bool (y_inverted)]) yield head if guides : for g in guides : if len (g) == 3 : wh, wt = g [:2] offset = off_scale * g [-1] * y_corr else : wh, wt = g offset = zero yield head * wh + tail * wt + offset yield tail
def intersection(self, other): """Returns intersection between the two lines in normal forms (None, if parallel). """ try: x = float(self.b * other.c - self.c * other.b) y = float(self.c * other.a - self.a * other.c) divisor = float(self.a * other.b - self.b * other.a) return D2.Point(x / divisor, y / divisor) except ArithmeticError: return None
def connection_point(self, point_1, point_2): """Returns the intersection point between the rectangle and the line between `point_1` and `point_2`. If no intersection exists (both points are either inside or outside) than None is returned. """ line = D2.Line(point_1, point_2) for side in (self.bottom, self.left, self.right, self.top): cp = side.intersection(line) if cp is not None: return cp return None
class Renderer(MOM.Graph._Renderer_): """ASCII renderer for MOM.Graph""" Canvas = Canvas default_grid_scale = D2.Point(2, 3) extension = "txt" node_size = D2.Point(16, 4) ### in characters conn_chars = dict \ ( bottom = "v" , left = "<" , right = ">" , top = "^" ) link_chars = dict \ ( Attr = dict (x = "_", y = ":") , IS_A = dict (x = ".", y = ".") , Role = dict (x = "-", y = "|") ) rect_chars = dict \ ( bottom = "-" , left = "|" , right = "|" , top = "-" ) def render(self): self.__super.render() return self.canvas.rendered() # end def render def render_link(self, link, canvas): chars = self.link_chars[link.relation.kind] head = link.points[0] tail = link.points[-1] side = link.relation.source_connector[0].side for line in pairwise(link.points): canvas.line(line, chars=chars) for p in link.points[1:-1]: canvas.text(p, "+") canvas.text(head, self.conn_chars[side]) canvas.text(tail, self.conn_chars[side]) # end def render_link def render_node(self, node, canvas): box = node.box pos = box.top_left + D2.Point(2, 1) width = box.size.x - 2 height = box.size.y - 2 def _label_parts(parts, width, height): i = 0 l = len(parts) x = "" while i < l and height > 0: p = x + parts[i] i += 1 while i < l and len(p) < width: if len(p) + len(parts[i]) < width: p += parts[i] i += 1 else: break yield p height -= 1 x = " " for lp in _label_parts(node.entity.label_parts, width, height): canvas.text(pos, lp) pos.shift((0, 1)) canvas.rectangle(box)
class Rect(TFL.Meta.Object, metaclass=M_Rect): """Model an axes-parallel rectangle in 2D space. >>> def rect_points (r) : ... for p in sorted (r.corner_dict.keys ()) : ... print ("%-20s : %s" % (p, getattr (r, p))) >>> def rect_sides (r) : ... for s in sorted (r.side_dict.keys ()) : ... print ("%-20s : %s" % (s, getattr (r, s))) >>> def connection_points (r) : ... P = D2.Point ... for p, off in sorted ( ... [ (r.top_left, P ( 0.0, +0.5)) ... , (r.top_left, P (-0.5, 0.0)) ... , (r.top_right, P ( 0.0, +0.5)) ... , (r.top_right, P ( 0.5, 0.0)) ... , (r.bottom_left, P ( 0.0, -0.5)) ... , (r.bottom_left, P (-0.5, 0.0)) ... , (r.bottom_right, P ( 0.0, -0.5)) ... , (r.bottom_right, P ( 0.5, 0.0)) ... ] ... , key = lambda x : tuple (x [0] + x [1]) ... ) : ... q = p + off ... print ("%s : %s" % (q, r.connection_point (q, r.center))) >>> q = Rect (D2.Point (1.0, 1.0), D2.Point (2.0, 1.0)) >>> rect_points (q) bottom_left : (1.0, 1.0) bottom_right : (3.0, 1.0) center : (2.0, 1.5) center_bottom : (2.0, 1.0) center_left : (1.0, 1.5) center_right : (3.0, 1.5) center_top : (2.0, 2.0) top_left : (1.0, 2.0) top_right : (3.0, 2.0) >>> q.scale (2) Rect ((1.0, 1.0), (4.0, 2.0)) >>> rect_points (q) bottom_left : (1.0, 1.0) bottom_right : (5.0, 1.0) center : (3.0, 2.0) center_bottom : (3.0, 1.0) center_left : (1.0, 2.0) center_right : (5.0, 2.0) center_top : (3.0, 3.0) top_left : (1.0, 3.0) top_right : (5.0, 3.0) >>> q.shift (D2.Point (1, 1)) Rect ((2.0, 2.0), (4.0, 2.0)) >>> rect_points (q) bottom_left : (2.0, 2.0) bottom_right : (6.0, 2.0) center : (4.0, 3.0) center_bottom : (4.0, 2.0) center_left : (2.0, 3.0) center_right : (6.0, 3.0) center_top : (4.0, 4.0) top_left : (2.0, 4.0) top_right : (6.0, 4.0) >>> rect_sides (q) bottom : ((2.0, 2.0), (6.0, 2.0)) left : ((2.0, 2.0), (2.0, 4.0)) right : ((6.0, 2.0), (6.0, 4.0)) top : ((2.0, 4.0), (6.0, 4.0)) >>> q = Rect (D2.Point (1.0, 1.0), D2.Point (1.0, 1.0)) >>> connection_points (q) (0.5, 1.0) : (1.0, 1.25) (0.5, 2.0) : (1.0, 1.75) (1.0, 0.5) : (1.25, 1.0) (1.0, 2.5) : (1.25, 2.0) (2.0, 0.5) : (1.75, 1.0) (2.0, 2.5) : (1.75, 2.0) (2.5, 1.0) : (2.0, 1.25) (2.5, 2.0) : (2.0, 1.75) """ Bottom_Left = D2.Point(0.0, 0.0) Bottom_Right = D2.Point(1.0, 0.0) Center = D2.Point(0.5, 0.5) Center_Bottom = D2.Point(0.5, 0.0) Center_Left = D2.Point(0.0, 0.5) Center_Right = D2.Point(1.0, 0.5) Center_Top = D2.Point(0.5, 1.0) Top_Left = D2.Point(0.0, 1.0) Top_Right = D2.Point(1.0, 1.0) side_dict = \ { "bottom" : (lambda r : D2.Line (r.bottom_left, r.bottom_right)) , "left" : (lambda r : D2.Line (r.bottom_left, r.top_left)) , "right" : (lambda r : D2.Line (r.bottom_right, r.top_right)) , "top" : (lambda r : D2.Line (r.top_left, r.top_right)) } @property def corners(self): return \ self.bottom_left, self. bottom_right, self.top_left, self.top_right # end def sides @property def ref_point(self): return self.bottom_left # end def ref_point @ref_point.setter def ref_point(self, value): self.bottom_left = value # end def ref_point def __init__(self, pos=(0.0, 0.0), size=(1.0, 1.0)): if isinstance(pos, (list, tuple)): pos = D2.Point(*pos) if isinstance(size, (list, tuple)): size = D2.Point(*size) self.ref_point = pos self.size = size # end def __init__ @classmethod def Sides(cls, diagonal, ratio): """Return the sides `(a, b)` of a rectangle with `diagonal` and `ratio` between the sides. """ b = math.sqrt(diagonal * diagonal / (1 + ratio * ratio)) a = b * ratio return a, b # end def Sides def connection_point(self, point_1, point_2): """Returns the intersection point between the rectangle and the line between `point_1` and `point_2`. If no intersection exists (both points are either inside or outside) than None is returned. """ line = D2.Line(point_1, point_2) for side in (self.bottom, self.left, self.right, self.top): cp = side.intersection(line) if cp is not None: return cp return None # end def connection_point def point(self, p=Center): """Return point at position `p` relative to the rectangle.""" return self.ref_point + (self.size * p) # end def point def point_in_rect(self, point): if isinstance(point, (list, tuple)): point = D2.Point(*point) tl = self.top_left br = tl + self.size if ((point.x < tl.x) or (point.x > br.x) or (point.y < tl.y) or (point.y > br.y)): return None return 1 # end def point_in_rect def scale(self, right): self.size.scale(right) return self # end def scale def shift(self, right): self.ref_point.shift(right) return self # end def shift def transformed(self, affine): """Return another rectangle whose coordinates are derived via `affine` transform from `self`. """ return self.__class__ \ (self.ref_point.transform (affine), self.size.transformed (affine)) # end def transformed def __getattr__(self, name): """Return the point or side `name`. The possible names are defined by `corner_dict` and `side_dict`. """ if name in self.corner_dict: return self.point(self.corner_dict[name]) elif name in self.side_dict: return self.side_dict[name](self) else: raise AttributeError(name) # end def __getattr__ def __repr__(self): return "%s %s" % (self.__class__.__name__, str(self)) # end def __repr__ def __str__(self): return "(%s, %s)" % (self.ref_point, self.size)
def rectangle(x, y, w, h): """Return a `Rect` at position `(x, y)` with size `(w, h)`""" return Rect(D2.Point(x, y), D2.Point(w, h))
class Renderer (MOM.Graph._Renderer_) : """SVG renderer for MOM.Graph""" default_grid_scale = D2.Point ( 2, 3) encoding = "utf-8" extension = "svg" link_markers = dict \ ( attr = dict (start = "MOM:AM") , is_a = dict (end = "MOM:IM") , role = dict (start = "MOM:RM") ) node_size = D2.Point (160, 100) want_document = True _Parameters = None def __init__ (self, graph, ** kw) : self.pop_to_self (kw, "encoding", "want_document") self.pop_to_self (kw, "Parameters", prefix = "_") self.__super.__init__ (graph, ** kw) # end def __init__ @property def Parameters (self) : result = self._Parameters if result is None : from _MOM._Graph.SVG_Parameters import SVG_Parameters result = self._Parameters = SVG_Parameters () return result # end def Parameters def Canvas (self, min_x, min_y, max_x, max_y) : P = self.Parameters graph = self.graph w, h = (max_x - min_x, max_y - min_y) result = SVG.Root \ ( klass = "MOM-Graph" , preserve_aspect_ratio = "xMinYMin" , view_box = "%d %d %d %d" % (min_x, min_y, w, h) , width = "100%" ) if self.want_document : result = SVG.Document \ ( result , encoding = self.encoding , standalone = False ) if graph.title : result.add (SVG.Title (graph.title)) elif graph.desc : result.add (SVG.Title (graph.desc)) defs = SVG.Defs \ ( SVG.Marker.Arrow_Head_Bar ( elid = "MOM:AM" , ref_x = P.attr_marker_ref_x , marker_width = P.attr_marker_size , marker_height = P.attr_marker_size , stroke = P.color.attr_link.no_alpha , stroke_opacity = P.color.attr_link.alpha , fill = P.color.link_bg.no_alpha , fill_opacity = P.color.link_bg.alpha ) , SVG.Marker.Arrow_Head ( elid = "MOM:IM" , ref_x = P.is_a_marker_ref_x , marker_width = P.is_a_marker_size , marker_height = P.is_a_marker_size , stroke = P.color.is_a_link.no_alpha , stroke_opacity = P.color.is_a_link.alpha ) , SVG.Marker.Plug ( elid = "MOM:RM" , stroke = P.color.role_link.no_alpha , stroke_opacity = P.color.role_link.alpha ) ) result.add (defs) return result # end def Canvas def render (self) : self.__super.render () return "\n".join (self.canvas.as_xml ()) # end def render def render_link (self, link, canvas) : P = self.Parameters rel = link.relation grp = SVG.Group \ ( elid = rel.rid , opacity = P.link_opacity ) lkind = link.relation.kind.lower () kw = dict \ ( ("marker_%s" % (k, ), "url(#%s)" % (v, )) for k, v in pyk.iteritems (self.link_markers.get (lkind, {})) ) colr = getattr (P.color, "%s_link" % lkind) paid = "%s::path" % (rel.rid, ) p, q = link.points [:2] p_q = p - q anchor, off = "start", 10 if max (abs (p_q)) < 5 * P.font_char_width and len (link.points) > 2 : p, q = link.points [1:3] p_q = p - q off = 2 if p_q.x > 0 : p, q = q, p anchor, off = "end", 90 offset = "%d%%" % off path = SVG.Path \ ( d = (p, q) , elid = paid , fill = "none" , stroke = "none" ) grp.add \ ( SVG.Title ("%s: %s" % (rel.title, rel.desc)) , SVG.Polyline ( fill = "none" , points = link.points , stroke = colr.no_alpha , stroke_width = P.link_stroke_width , stroke_opacity = colr.alpha , ** kw ) , path , SVG.Text ( SVG.Text_Path ( SVG.Tspan (rel.label, dy = - P.font_char_width * 3 // 4) , start_offset = offset , text_anchor = anchor , xlink_href = "#%s" % paid ) , fill = colr.no_alpha , fill_opacity = colr.alpha , font_family = P.font_family , font_size = P.font_size ) # rel.info ) if rel.info : grp.add \ ( SVG.Text ( SVG.Text_Path ( SVG.Tspan (rel.info, dy = P.font_size) , start_offset = offset , text_anchor = anchor , xlink_href = "#%s" % paid ) , fill = colr.no_alpha , fill_opacity = colr.alpha , font_family = P.font_family , font_size = P.font_size ) ) canvas.add (grp) # end def render_link def render_node (self, node, canvas) : P = self.Parameters box = node.box grp = SVG.Group \ ( elid = node.entity.type_name , fill = P.color.node_bg , klass = "E_Type" , opacity = P.partial_node_opacity if node.entity.is_partial else P.node_opacity ) grp.add \ ( SVG.Title ("%s: %s" % (node.entity.title, node.entity.desc)) , SVG.Rect ( x = box.ref_point.x , y = box.ref_point.y , width = box.size.x , height = box.size.y , stroke = P.color.node_border , stroke_width = P.node_border_width ) ) self._render_node_labels (node, grp) canvas.add (grp) # end def render_node def _render_node_labels (self, node, grp) : P = self.Parameters box = node.box tp = box.ref_point + D2.Point (P.font_size // 2, 0) width = int ((box.size.x - P.font_size) / P.font_char_width) height = box.size.y / P.line_height - 1 def _label_parts (parts, width, height) : i = 0 l = len (parts) x = "" while i < l and height > 0: p = x + parts [i] i += 1 while i < l and len (p) < width : if len (p) + len (parts [i]) < width : p += parts [i] i += 1 else : break yield p height -= 1 txt = SVG.Text \ ( x = tp.x , y = tp.y , fill = P.color.text , font_family = P.font_family , font_size = P.font_size ) dx = 0 for lp in _label_parts (node.entity.label_parts, width, height) : txt.add (SVG.Tspan (lp, x = tp.x, dx = dx, dy = P.line_height)) dx = P.font_size // 2 grp.add (txt)
def __getattr__ (self, name) : """Return the point or side `name`. The possible names are defined by `corner_dict` and `side_dict`. """ if name in self.corner_dict : return self.point (self.corner_dict [name]) elif name in self.side_dict : return self.side_dict [name] (self) else : raise AttributeError (name) # end def __getattr__ def __repr__ (self) : return "%s %s" % (self.__class__.__name__, str (self)) # end def __repr__ def __str__ (self) : return "(%s, %s)" % (self.ref_point, self.size) # end def __str__ # end class Rect def rectangle (x, y, w, h) : """Return a `Rect` at position `(x, y)` with size `(w, h)`""" return Rect (D2.Point (x, y), D2.Point (w, h)) # end def rectangle if __name__ != "__main__" : D2._Export ("*") ### __END__ Rect
def __init__ \ ( self, ref_points, x_weights, y_weights , offset = None, scale = None ) : if not (len (ref_points) == len (x_weights) == len (y_weights)) : raise ValueError \ ( "%s must have equal length" % ((ref_points, x_weights, y_weights), ) ) self._ref_points = ref_points self._x_weights = x_weights self._y_weights = y_weights self.__super.__init__ (offset, scale) # end def __init__ def _reference (self) : return (self._ref_points, self._x_weights, self._y_weights) # end def _reference # end class R_Point_nP P = Point Pp = R_Point_P Pl = R_Point_L Pr = R_Point_R Pn = R_Point_nP if __name__ != "__main__" : D2._Export ("*", "_Point_", "_R_Point_", "P", "Pp", "Pl", "Pr", "Pn") ### __END__ TFL.D2.Point
class _Screen_Rect_(D2.Rect): """Model an axes-parallel rectangle in 2D screen coordinates. >>> def rect_points (r) : ... for p in sorted (r.corner_dict.keys ()) : ... print ("%-20s : %s" % (p, getattr (r, p))) >>> def rect_sides (r) : ... for s in sorted (r.side_dict.keys ()) : ... print ("%-20s : %s" % (s, getattr (r, s))) >>> def connection_points (r) : ... P = D2.Point ... for p, off in sorted ( ... [ (r.top_left, P ( 0.0, -0.5)) ... , (r.top_left, P (-0.5, 0.0)) ... , (r.top_right, P ( 0.0, -0.5)) ... , (r.top_right, P ( 0.5, 0.0)) ... , (r.bottom_left, P ( 0.0, 0.5)) ... , (r.bottom_left, P (-0.5, 0.0)) ... , (r.bottom_right, P ( 0.0, 0.5)) ... , (r.bottom_right, P ( 0.5, 0.0)) ... ] ... , key = lambda x : tuple (x [0] + x [1]) ... ) : ... q = p + off ... print ("%s : %s" % (q, r.connection_point (q, r.center))) >>> q = Rect (D2.Point (1.0, 1.0), D2.Point (2.0, 1.0)) >>> rect_points (q) bottom_left : (1.0, 2.0) bottom_right : (3.0, 2.0) center : (2.0, 1.5) center_bottom : (2.0, 2.0) center_left : (1.0, 1.5) center_right : (3.0, 1.5) center_top : (2.0, 1.0) top_left : (1.0, 1.0) top_right : (3.0, 1.0) >>> q.scale (2) Rect ((1.0, 1.0), (4.0, 2.0)) >>> rect_points (q) bottom_left : (1.0, 3.0) bottom_right : (5.0, 3.0) center : (3.0, 2.0) center_bottom : (3.0, 3.0) center_left : (1.0, 2.0) center_right : (5.0, 2.0) center_top : (3.0, 1.0) top_left : (1.0, 1.0) top_right : (5.0, 1.0) >>> q.shift (D2.Point (1, 1)) Rect ((2.0, 2.0), (4.0, 2.0)) >>> rect_points (q) bottom_left : (2.0, 4.0) bottom_right : (6.0, 4.0) center : (4.0, 3.0) center_bottom : (4.0, 4.0) center_left : (2.0, 3.0) center_right : (6.0, 3.0) center_top : (4.0, 2.0) top_left : (2.0, 2.0) top_right : (6.0, 2.0) >>> rect_sides (q) bottom : ((2.0, 4.0), (6.0, 4.0)) left : ((2.0, 2.0), (2.0, 4.0)) right : ((6.0, 2.0), (6.0, 4.0)) top : ((2.0, 2.0), (6.0, 2.0)) >>> q = Rect (D2.Point (1.0, 1.0), D2.Point (1.0, 1.0)) >>> connection_points (q) (0.5, 1.0) : (1.0, 1.25) (0.5, 2.0) : (1.0, 1.75) (1.0, 0.5) : (1.25, 1.0) (1.0, 2.5) : (1.25, 2.0) (2.0, 0.5) : (1.75, 1.0) (2.0, 2.5) : (1.75, 2.0) (2.5, 1.0) : (2.0, 1.25) (2.5, 2.0) : (2.0, 1.75) """ _real_name = "Rect" Bottom_Left = D2.Point(0.0, 1.0) Bottom_Right = D2.Point(1.0, 1.0) Center = D2.Point(0.5, 0.5) Center_Bottom = D2.Point(0.5, 1.0) Center_Left = D2.Point(0.0, 0.5) Center_Right = D2.Point(1.0, 0.5) Center_Top = D2.Point(0.5, 0.0) Top_Left = D2.Point(0.0, 0.0) Top_Right = D2.Point(1.0, 0.0) side_dict = \ { "bottom" : (lambda r : D2.Line (r.bottom_left, r.bottom_right)) , "left" : (lambda r : D2.Line (r.top_left, r.bottom_left)) , "right" : (lambda r : D2.Line (r.top_right, r.bottom_right)) , "top" : (lambda r : D2.Line (r.top_left, r.top_right)) } @property def ref_point(self): return self.top_left # end def ref_point @ref_point.setter def ref_point(self, value): self.top_left = value
class _Renderer_(TFL.Meta.Object): """Base class for MOM.Graph renderers.""" node_size = D2.Point(90, 20) default_grid_scale = D2.Point(2, 4) _grid_size = None Canvas = None ### redefine in descendants Link = Link Node = Node def __init__(self, graph, **kw): self.pop_to_self(kw, "node_size", "default_grid_scale") self.pop_to_self(kw, "grid_size", prefix="_") self.graph = graph Node = self.Node g_nodes = self.graph.nodes() self.nodes = ns = list(Node(e, self) for e in g_nodes) self.node_map = dict((n.entity.type_name, n) for n in ns) graph.setup_links() for n in ns: n.setup_links() self.canvas = self.Canvas \ (self.min_x, self.min_y, self.max_x, self.max_y) # end def __init__ @property def grid_size(self): result = self._grid_size if result is None: result = self._grid_size = self.node_size * self.default_grid_scale return result # end def grid_size @grid_size.setter def grid_size(self, value): self._grid_size = value # end def grid_size @TFL.Meta.Once_Property def max_x(self): return int(max(n.max_x for n in self.nodes) + self.grid_size.x // 4) # end def max_x @TFL.Meta.Once_Property def max_x_spec(self): return max(v.pos.x for v in pyk.itervalues(self.graph.node_map)) # end def max_x_spec @TFL.Meta.Once_Property def max_y(self): return int(max(n.max_y for n in self.nodes) + self.grid_size.y // 4) # end def max_y @TFL.Meta.Once_Property def max_y_spec(self): return max(v.pos.y for v in pyk.itervalues(self.graph.node_map)) # end def max_y_spec @TFL.Meta.Once_Property def min_x(self): result = int(min(n.min_x for n in self.nodes) - self.grid_size.x // 4) return max(result, 0) # end def min_x @TFL.Meta.Once_Property def min_x_spec(self): return min(v.pos.x for v in pyk.itervalues(self.graph.node_map)) # end def min_x_spec @TFL.Meta.Once_Property def min_y(self): result = int(min(n.min_y for n in self.nodes) - self.grid_size.y // 4) return max(result, 0) # end def min_y @TFL.Meta.Once_Property def min_y_spec(self): return min(v.pos.y for v in pyk.itervalues(self.graph.node_map)) # end def min_y_spec @TFL.Meta.Once_Property def transform(self): dx = -self.min_x_spec ### shift minimum to zero dy = -self.max_y_spec ### shift maximum to zero: to be reflected gs = self.grid_size result = \ ( D2.Affine.Trans (gs.x // 4, gs.y // 4) * D2.Affine.Scale (gs.x, gs.y) * D2.Affine.Reflection (1, 0) * D2.Affine.Trans (dx, dy) ) return result # end def transform def render(self): canvas = self.canvas nodes = sorted(self.nodes, key=TFL.Getter.entity.type_name) for n in nodes: self.render_node(n, canvas) link_sort_key = TFL.Sorted_By("relation.rid") for n in nodes: for l in sorted(pyk.itervalues(n.link_map), key=link_sort_key): self.render_link(l, canvas) # end def render def render_link(self, link, canvas): raise NotImplementedError \ ("%s needs to implement render_link" % (self.__class__.__name__, )) # end def render_link def render_node(self, node, canvas): raise NotImplementedError \ ("%s needs to implement render_node" % (self.__class__.__name__, ))
return self.side_dict[name](self) else: raise AttributeError(name) # end def __getattr__ def __repr__(self): return "%s %s" % (self.__class__.__name__, str(self)) # end def __repr__ def __str__(self): return "(%s, %s)" % (self.ref_point, self.size) # end def __str__ # end class Rect def rectangle(x, y, w, h): """Return a `Rect` at position `(x, y)` with size `(w, h)`""" return Rect(D2.Point(x, y), D2.Point(w, h)) # end def rectangle if __name__ != "__main__": D2._Export("*") ### __END__ Rect