Example #1
0
def init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root):
    # ::: Precalculate values :::
    visited = set()
    to_visit = []
    to_visit.append(root_node)
    last_rotation = img.arc_start
    depth = 1
    while to_visit:
        node = to_visit[-1]
        finished = True
        if node not in n2i:
            # Set style according to layout function
            item = n2i[node] = _NodeItem(node, parent.tree_layer)
            depth += 1

            item.setZValue(depth)
            if node is root_node and hide_root:
                pass
            else:
                init_node_dimensions(node, item, n2f[node], img)
                #set_node_size(node, n2i, n2f, img)

        if not _leaf(node):
            # visit children starting from left to right. Very
            #  important!! check all children[-1] and children[0]
            for c in reversed(node.children):
                if c not in visited:
                    to_visit.append(c)
                    finished = False
            # :: pre-order code here ::
        if not finished:
            continue
        else:
            to_visit.pop(-1)
            visited.add(node)

        # :: Post-order visits. Leaves are already visited here ::
        if img.mode == "c":
            if _leaf(node):
                crender.init_circular_leaf_item(node, n2i, n2f, last_rotation,
                                                rot_step)
                last_rotation += rot_step
            else:
                crender.init_circular_node_item(node, n2i, n2f)

        elif img.mode == "r":
            if _leaf(node):
                rrender.init_rect_leaf_item(node, n2i, n2f)
            else:
                rrender.init_rect_node_item(node, n2i, n2f)
Example #2
0
def init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root):
    # ::: Precalculate values :::
    visited = set()
    to_visit = []
    to_visit.append(root_node)
    last_rotation = img.arc_start
    depth = 1
    while to_visit:
        node = to_visit[-1]
        finished = True
        if node not in n2i:
            # Set style according to layout function
            item = n2i[node] = _NodeItem(node, parent.tree_layer)
            depth += 1

            item.setZValue(depth)
            if node is root_node and hide_root:
                pass
            else:
                init_node_dimensions(node, item, n2f[node], img)
                #set_node_size(node, n2i, n2f, img)

        if not _leaf(node):
            # visit children starting from left to right. Very
            #  important!! check all children[-1] and children[0]
            for c in reversed(node.children):
                if c not in visited:
                    to_visit.append(c)
                    finished = False
            # :: pre-order code here ::
        if not finished:
            continue
        else:
            to_visit.pop(-1)
            visited.add(node)

        # :: Post-order visits. Leaves are already visited here ::
        if img.mode == "c":
            if _leaf(node):
                crender.init_circular_leaf_item(node, n2i, n2f, last_rotation, rot_step)
                last_rotation += rot_step
            else:
                crender.init_circular_node_item(node, n2i, n2f)

        elif img.mode == "r":
            if _leaf(node):
                rrender.init_rect_leaf_item(node, n2i, n2f)
            else:
                rrender.init_rect_node_item(node, n2i, n2f)
def get_partition_center(n, n2i, n2f):
    down_h = n2f[n]["branch-bottom"].h
    up_h = n2f[n]["branch-top"].h

    #right_h = max(n2f[n]["branch-right"].h, n.img_style["size"]) /2
    right_h = n2i[n].nodeRegion.height() / 2

    up_h = max(right_h, up_h)
    down_h = max(right_h, down_h)

    fullR = n2i[n].fullRegion

    if _leaf(n):
        center = fullR.height() / 2
    else:
        first_child_part = n2i[n.children[0]]
        last_child_part = n2i[n.children[-1]]
        c1 = first_child_part.start_y + first_child_part.center
        c2 = last_child_part.start_y + last_child_part.center
        center = c1 + ((c2 - c1) / 2)

    if up_h > center:
        center = up_h
    elif down_h > fullR.height() - center:
        center = fullR.height() - down_h

    return center
Example #4
0
def get_partition_center(n, n2i, n2f):
        down_h = n2f[n]["branch-bottom"].h
        up_h = n2f[n]["branch-top"].h

        #right_h = max(n2f[n]["branch-right"].h, n.img_style["size"]) /2
        right_h = n2i[n].nodeRegion.height()/2

        up_h = max(right_h, up_h)
        down_h = max(right_h, down_h)
        
        fullR = n2i[n].fullRegion

        if _leaf(n):
            center = fullR.height()/2
        else:
            first_child_part = n2i[n.children[0]]
            last_child_part = n2i[n.children[-1]]
            c1 = first_child_part.start_y + first_child_part.center
            c2 = last_child_part.start_y + last_child_part.center
            center = c1 + ((c2-c1)/2)

        if up_h > center:
            center = up_h
        elif down_h > fullR.height() - center:
            center = fullR.height() - down_h

        return center
Example #5
0
def render_backgrounds(img, mainRect, bg_layer, n2i, n2f):

    if img.mode == "c":
        max_r = mainRect.width()/2.0
    else:
        max_r = mainRect.width()

    for node, item in n2i.iteritems():
        if _leaf(node):
            first_c = n2i[node]
            last_c = n2i[node]
        else:
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]

        if img.mode == "c":
            h = item.effective_height
            angle_start = first_c.full_start
            angle_end = last_c.full_end
            parent_radius = getattr(n2i.get(node.up, None), "radius", 0)
            base = parent_radius + item.nodeRegion.width()

            if node.img_style["node_bgcolor"].upper() != "#FFFFFF":
                bg1 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2)
                bg1.set_arc(0, 0, parent_radius, r, angle_start, angle_end)
                bg1.setParentItem(item.content.bg)
                bg1.setPen(QtGui.QPen(QtGui.QColor(node.img_style["node_bgcolor"])))
                bg1.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["node_bgcolor"])))

            if node.img_style["faces_bgcolor"].upper() != "#FFFFFF":
                bg2 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2)
                bg2.set_arc(0, 0, parent_radius, item.radius, angle_start, angle_end)
                bg2.setParentItem(item.content)
                bg2.setPen(QtGui.QPen(QtGui.QColor(node.img_style["faces_bgcolor"])))
                bg2.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["faces_bgcolor"])))

            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = crender._ArcItem()
                bg.set_arc(0, 0, parent_radius, max_r, angle_start, angle_end)
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())

        if img.mode == "r":
            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = QtGui.QGraphicsRectItem()
                pos = item.content.mapToScene(0, 0)
                bg.setPos(pos.x(), pos.y())
                bg.setRect(0, 0, max_r-pos.x(),  item.fullRegion.height())
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())
Example #6
0
def render_backgrounds(img, mainRect, bg_layer, n2i, n2f):

    if img.mode == "c":
        max_r = mainRect.width()/2.0
    else:
        max_r = mainRect.width()

    for node, item in n2i.iteritems():
        if _leaf(node):
            first_c = n2i[node]
            last_c = n2i[node]
        else:
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]

        if img.mode == "c":
            h = item.effective_height
            angle_start = first_c.full_start
            angle_end = last_c.full_end
            parent_radius = getattr(n2i.get(node.up, None), "radius", 0)
            base = parent_radius + item.nodeRegion.width()

            if node.img_style["node_bgcolor"].upper() != "#FFFFFF":
                bg1 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2)
                bg1.set_arc(0, 0, parent_radius, r, angle_start, angle_end)
                bg1.setParentItem(item.content.bg)
                bg1.setPen(QtGui.QPen(QtGui.QColor(node.img_style["node_bgcolor"])))
                bg1.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["node_bgcolor"])))

            if node.img_style["faces_bgcolor"].upper() != "#FFFFFF":
                bg2 = crender._ArcItem()
                r = math.sqrt(base**2 + h**2)
                bg2.set_arc(0, 0, parent_radius, item.radius, angle_start, angle_end)
                bg2.setParentItem(item.content)
                bg2.setPen(QtGui.QPen(QtGui.QColor(node.img_style["faces_bgcolor"])))
                bg2.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["faces_bgcolor"])))

            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = crender._ArcItem()
                bg.set_arc(0, 0, parent_radius, max_r, angle_start, angle_end)
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())

        if img.mode == "r":
            if node.img_style["bgcolor"].upper() != "#FFFFFF":
                bg = QtGui.QGraphicsRectItem()
                pos = item.content.mapToScene(0, 0)
                bg.setPos(pos.x(), pos.y())
                bg.setRect(0, 0, max_r-pos.x(),  item.fullRegion.height())
                bg.setPen(QtGui.QPen(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setBrush(QtGui.QBrush(QtGui.QColor(node.img_style["bgcolor"])))
                bg.setParentItem(bg_layer)
                bg.setZValue(item.zValue())
Example #7
0
def update_branch_lengths(tree, n2i, n2f, img):
    for node in tree.traverse("postorder", is_leaf_fn=_leaf):
        item = n2i[node]
        ndist = 1.0 if img.force_topology else node.dist
        item.branch_length = ndist * img._scale
        w0 = 0
        
        if item.branch_length > item.widths[1]:
            w0 = item.widths[0] = item.branch_length - item.widths[1]
            item.nodeRegion.adjust(0, 0, w0, 0)
            
        child_width = 0
        if not _leaf(node):
            for ch in node.children:
                child_width = max(child_width, n2i[ch].fullRegion.width())
                if w0 and img.mode == "r":
                    n2i[ch].translate(w0, 0)
        item.fullRegion.setWidth(item.nodeRegion.width() + child_width)
Example #8
0
def update_branch_lengths(tree, n2i, n2f, img):
    for node in tree.traverse("postorder", is_leaf_fn=_leaf):
        item = n2i[node]
        ndist = 1.0 if img.force_topology else node.dist
        item.branch_length = ndist * img._scale
        w0 = 0
        
        if item.branch_length > item.widths[1]:
            w0 = item.widths[0] = item.branch_length - item.widths[1]
            item.nodeRegion.adjust(0, 0, w0, 0)
            
        child_width = 0
        if not _leaf(node):
            for ch in node.children:
                child_width = max(child_width, n2i[ch].fullRegion.width())
                if w0 and img.mode == "r":
                    n2i[ch].translate(w0, 0)
        item.fullRegion.setWidth(item.nodeRegion.width() + child_width)
Example #9
0
def render_node_content(node, n2i, n2f, img):
    style = node.img_style
    item = n2i[node]
    item.content = _EmptyItem(item)

    nodeR = item.nodeRegion
    facesR = item.facesRegion
    center = item.center
    branch_length = item.branch_length

    # Node points
    ball_size = style["size"]
    
    vlw = style["vt_line_width"] if not _leaf(node) and len(node.children) > 1 else 0.0
    
    face_start_x = nodeR.width() - facesR.width() - vlw
    ball_start_x = face_start_x - ball_size 
    
    if ball_size:
        if node.img_style["shape"] == "sphere":
            node_ball = _SphereItem(node)
        elif node.img_style["shape"] == "circle":
            node_ball = _CircleItem(node)
        elif node.img_style["shape"] == "square":
            node_ball = _RectItem(node)

        node_ball.setPos(ball_start_x, center-(ball_size/2.0))

        #from qt4_gui import _BasicNodeActions
        #node_ball.delegate = _BasicNodeActions()
        #node_ball.setAcceptsHoverEvents(True)
        #node_ball.setCursor(QtCore.Qt.PointingHandCursor)

    else:
        node_ball = None

    # Branch line to parent
    pen = QtGui.QPen()
    set_pen_style(pen, style["hz_line_type"])
    pen.setColor(QtGui.QColor(style["hz_line_color"]))
    pen.setWidth(style["hz_line_width"])
    pen.setCapStyle(QtCore.Qt.FlatCap)
    #pen.setCapStyle(QtCore.Qt.RoundCap)
    #pen.setCapStyle(QtCore.Qt.SquareCap)
    #pen.setJoinStyle(QtCore.Qt.RoundJoin)
    hz_line = _LineItem()
    hz_line = _NodeLineItem(node)
    hz_line.setPen(pen)

    join_fix = 0
    if node.up and node.up.img_style["vt_line_width"]:
        join_fix = node.up.img_style["vt_line_width"] 

    hz_line.setLine(-join_fix, center, branch_length, center)

    if img.complete_branch_lines_when_necessary:
        extra_line = _LineItem(branch_length, center, ball_start_x, center)
        pen = QtGui.QPen()
        item.extra_branch_line = extra_line
        set_pen_style(pen, img.extra_branch_line_type)
        pen.setColor(QtGui.QColor(img.extra_branch_line_color))
        pen.setCapStyle(QtCore.Qt.FlatCap)
        pen.setWidth(style["hz_line_width"])
        extra_line.setPen(pen)
    else:
        extra_line = None

    # Attach branch-right faces to child
    fblock_r = n2f[node]["branch-right"]
    fblock_r.render()
    fblock_r.setPos(face_start_x, center-fblock_r.h/2.0)

    # Attach branch-bottom faces to child
    fblock_b = n2f[node]["branch-bottom"]
    fblock_b.render()
    fblock_b.setPos(item.widths[0], center + style["hz_line_width"]/2.0)

    # Attach branch-top faces to child
    fblock_t = n2f[node]["branch-top"]
    fblock_t.render()
    fblock_t.setPos(item.widths[0], center - fblock_t.h - style["hz_line_width"]/2.0)

    # Vertical line
    if not _leaf(node):
        if img.mode == "c":
            vt_line = QtGui.QGraphicsPathItem()

        elif img.mode == "r":
            vt_line = _LineItem(item)
            first_child = node.children[0]
            last_child = node.children[-1]
            first_child_part = n2i[node.children[0]]
            last_child_part = n2i[node.children[-1]]
            c1 = first_child_part.start_y + first_child_part.center
            c2 = last_child_part.start_y + last_child_part.center
            fx = nodeR.width() - (vlw/2.0)
            if first_child.img_style["hz_line_width"] > 0:
                c1 -= (first_child.img_style["hz_line_width"] / 2.0)
            if last_child.img_style["hz_line_width"] > 0:
                c2 += (last_child.img_style["hz_line_width"] / 2.0)
            vt_line.setLine(fx, c1, fx, c2)

        pen = QtGui.QPen()
        set_pen_style(pen, style["vt_line_type"])
        pen.setColor(QtGui.QColor(style["vt_line_color"]))
        pen.setWidth(style["vt_line_width"])
        pen.setCapStyle(QtCore.Qt.FlatCap)
        #pen.setCapStyle(QtCore.Qt.RoundCap)
        #pen.setCapStyle(QtCore.Qt.SquareCap)
        vt_line.setPen(pen)
        item.vt_line = vt_line
    else:
        vt_line = None

    item.bg = QtGui.QGraphicsItemGroup()
    item.movable_items = [] #QtGui.QGraphicsItemGroup()
    item.static_items = [] #QtGui.QGraphicsItemGroup()

    # Items fow which coordinates are exported in the image map
    item.mapped_items = [node_ball, fblock_r, fblock_b, fblock_t]

    for i in [node_ball, fblock_r, fblock_b, fblock_t]:
        if i:
            #item.movable_items.addToGroup(i)
            item.movable_items.append(i)
            i.setParentItem(item.content)

    for i in [vt_line, extra_line, hz_line]:
        if i:
            #item.static_items.addToGroup(i)
            item.static_items.append(i)
            i.setParentItem(item.content)
Example #10
0
def set_node_size(node, n2i, n2f, img):
    scale = img._scale
    min_separation = img.min_leaf_separation

    item = n2i[node]
    if img.force_topology:
        branch_length = item.branch_length = 25
    else:
        branch_length = item.branch_length = float(node.dist * scale)

    # Organize faces by groups
    #faceblock = update_node_faces(node, n2f, img)
    faceblock = n2f[node]
    aligned_height = 0
    if _leaf(node):
        if img.mode == "r":
            aligned_height = faceblock["aligned"].h
        elif img.mode == "c":
            # aligned faces in circular mode are adjusted afterwords. The
            # min radius of the largest aligned faces will be calculated.
            pass

    # Total height required by the node. I cannot sum up the height of
    # all elements, since the position of some of them are forced to
    # be on top or at the bottom of branches. This fact can produce
    # and unbalanced nodeRegion center. Here, I only need to know
    # about the imbalance size to correct node height. The center will
    # be calculated later according to the parent position.
    top_half_h = ( (node.img_style["size"]/2.0) +
                       node.img_style["hz_line_width"]/2.0 +
                       faceblock["branch-top"].h )

    bottom_half_h =( (node.img_style["size"]/2.0) +
                       node.img_style["hz_line_width"]/2.0 +
                       faceblock["branch-bottom"].h )

    h1 = top_half_h + bottom_half_h
    h2 = max(faceblock["branch-right"].h, \
                 aligned_height, \
                min_separation )
    h = max(h1, h2)
    imbalance = abs(top_half_h - bottom_half_h)
    if imbalance > h2/2.0:
        h += imbalance - (h2/2.0)

    # This adds a vertical margin around the node elements
    h += img.branch_vertical_margin

    # Total width required by the node
    w = sum([max(branch_length + node.img_style["size"],
                 faceblock["branch-top"].w + node.img_style["size"],
                 faceblock["branch-bottom"].w + node.img_style["size"],
                 ),
             faceblock["branch-right"].w]
            )
    w += node.img_style["vt_line_width"]

    # rightside faces region
    item.facesRegion.setRect(0, 0, faceblock["branch-right"].w, h)

    # Node region
    item.nodeRegion.setRect(0, 0, w, h)

    # This is the node total region covered by the node
    item.fullRegion.setRect(0, 0, w, h)
Example #11
0
def render(root_node, img, hide_root=False):
    '''main render function. hide_root option is used when render
    trees as Faces

    '''
    mode = img.mode
    orientation = img.orientation

    arc_span = img.arc_span

    layout_fn = img._layout_handler

    parent = _TreeItem()
    n2i = parent.n2i # node to items
    n2f = parent.n2f # node to faces

    parent.bg_layer =  _EmptyItem(parent)
    parent.tree_layer = _EmptyItem(parent)
    parent.float_layer = _EmptyItem(parent)
    parent.float_behind_layer = _EmptyItem(parent)

    TREE_LAYERS = [parent.bg_layer, parent.tree_layer,
                   parent.float_layer, parent.float_behind_layer]

    parent.bg_layer.setZValue(0)
    parent.tree_layer.setZValue(2)

    parent.float_behind_layer.setZValue(1)
    parent.float_layer.setZValue(3)

    # This could be used to handle aligned faces in internal
    # nodes.
    virtual_leaves = 0

    if img.show_branch_length:
        bl_face = faces.AttrFace("dist", fsize=8, ftype="Arial", fgcolor="black", formatter = "%0.3g")
    if img.show_branch_support:
        su_face = faces.AttrFace("support", fsize=8, ftype="Arial", fgcolor="darkred", formatter = "%0.3g")
    if img.show_leaf_name:
        na_face = faces.AttrFace("name", fsize=10, ftype="Arial", fgcolor="black")
    
    for n in root_node.traverse(is_leaf_fn=_leaf):
        set_style(n, layout_fn)

        if img.show_branch_length:
            faces.add_face_to_node(bl_face, n, 0, position="branch-top")

        if not _leaf(n) and img.show_branch_support:
            faces.add_face_to_node(su_face, n, 0, position="branch-bottom")

        if _leaf(n) and img.show_leaf_name:
            faces.add_face_to_node(na_face, n, 0, position="branch-right")

        if _leaf(n):# or len(n.img_style["_faces"]["aligned"]):
            virtual_leaves += 1

        update_node_faces(n, n2f, img)

    rot_step = float(arc_span) / virtual_leaves
    #rot_step = float(arc_span) / len([n for n in root_node.traverse() if _leaf(n)])

    # Calculate optimal branch length
    if img._scale is not None:
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
    elif img.scale is None:
        # create items and calculate node dimensions skipping branch lengths
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
        if mode == 'r':
            if img.optimal_scale_level == "full":
                scales = [(i.widths[1]/n.dist) for n,i in n2i.iteritems() if n.dist]
                img._scale = max(scales) if scales else 0.0
            else:
                farthest, dist = root_node.get_farthest_leaf(topology_only=img.force_topology)
                img._scale = img.tree_width / dist if dist else 0.0
            update_branch_lengths(root_node, n2i, n2f, img)
        else:
            img._scale = crender.calculate_optimal_scale(root_node, n2i, rot_step, img)
            #print "OPTIMAL circular scale", img._scale
            update_branch_lengths(root_node, n2i, n2f, img)
            init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
    else:
        # create items and calculate node dimensions CONSIDERING branch lengths
        img._scale = img.scale
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
        
    #print "USING scale", img._scale
    # Draw node content
    for node in root_node.traverse(is_leaf_fn=_leaf):
        if node is not root_node or not hide_root:
            render_node_content(node, n2i, n2f, img)

    # Adjust content to rect or circular layout
    mainRect = parent.rect()

    if mode == "c":
        tree_radius = crender.render_circular(root_node, n2i, rot_step)
        mainRect.adjust(-tree_radius, -tree_radius, tree_radius, tree_radius)
    else:
        iwidth = n2i[root_node].fullRegion.width()
        iheight = n2i[root_node].fullRegion.height()
        mainRect.adjust(0, 0, iwidth, iheight)
        tree_radius = iwidth

    # Add extra layers: aligned faces, floating faces, node
    # backgrounds, etc. The order by which the following methods are
    # called IS IMPORTANT
    render_floatings(n2i, n2f, img, parent.float_layer, parent.float_behind_layer)

    aligned_region_width = render_aligned_faces(img, mainRect, parent.tree_layer, n2i, n2f)

    render_backgrounds(img, mainRect, parent.bg_layer, n2i, n2f)

    # rotate if necessary in circular images. flip and adjust if mirror orientation. 
    adjust_faces_to_tranformations(img, mainRect, n2i, n2f, TREE_LAYERS)

    # Rotate main image if necessary
    parent.setRect(mainRect)
    parent.setPen(QtGui.QPen(QtCore.Qt.NoPen))

    if img.rotation:
        rect = parent.boundingRect()
        x =  rect.x() + (rect.width()/2.0)
        y =  rect.y() +  (rect.height()/2.0)
        parent.setTransform(QtGui.QTransform().translate(x, y).rotate(img.rotation).translate(-x, -y))

    # Creates the main tree item that will act as frame for the whole image
    frame = QtGui.QGraphicsRectItem()
    parent.setParentItem(frame)
    mainRect = parent.mapToScene(mainRect).boundingRect()

    mainRect.adjust(-img.margin_left, -img.margin_top, \
                         img.margin_right, img.margin_bottom)

    # Fix negative coordinates, so main item always starts at 0,0
    topleft  = mainRect.topLeft()
    _x = abs(topleft.x()) if topleft.x() < 0 else 0
    _y = abs(topleft.y()) if topleft.y() < 0 else 0
    if _x or _y:
        parent.moveBy(_x, _y)
        mainRect.adjust(_x, _y, _x, _y)
        
    # Add extra components and adjust mainRect to them
    add_legend(img, mainRect, frame)
    add_title(img, mainRect, frame)
    add_scale(img, mainRect, frame)
    frame.setRect(mainRect)

    # Draws a border around the tree
    if not img.show_border:
        frame.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    else:
        frame.setPen(QtGui.QPen(QtGui.QColor("black")))
    
    return frame, n2i, n2f
Example #12
0
def render_circular(root_node, n2i, rot_step):
    to_visit = []
    to_visit.append(root_node)
    max_r = 0.0
    while to_visit:
        node = to_visit.pop(0)

        if not _leaf(node):
            to_visit.extend(node.children)

        item = n2i[node]
        w = item.nodeRegion.width()
        h = item.effective_height

        if node is not root_node:
            parent_radius = n2i[node.up].radius
        else:
            parent_radius = 0

        if _leaf(node):
            angle = rot_step
        else:
            angle = item.angle_span
            #full_angle = angle
            #full_angle = abs(item.full_end - item.full_start)

        r, xoffset = get_min_radius(w, h, angle, parent_radius)
        rotate_and_displace(item.content, item.rotation, h, parent_radius)
        item.radius = r
        max_r = max(max_r, r)

        if not _leaf(node):
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]
            # Vertical arc Line
            rot_end = n2i[node.children[-1]].rotation
            rot_start = n2i[node.children[0]].rotation

            C = item.vt_line
            C.setParentItem(item)
            path = QtGui.QPainterPath()
            # Counter clock wise
            path.arcMoveTo(-r, -r, r * 2, r * 2, 360 - rot_start - angle)
            path.arcTo(-r, -r, r*2, r * 2, 360 - rot_start - angle, angle)
            # Faces
            C.setPath(path)
            item.static_items.append(C)

        if hasattr(item, "content"):

            # If applies, it sets the length of the extra branch length
            if item.extra_branch_line:
                xtra =  item.extra_branch_line.line().dx()
                if xtra > 0:
                    xtra = xoffset + xtra
                else:
                    xtra = xoffset 
                item.extra_branch_line.setLine(item.branch_length, item.center,\
                                                   item.branch_length + xtra , item.center)
                item.nodeRegion.setWidth(item.nodeRegion.width()+xtra)

            # And moves elements 
            if xoffset:
                for i in item.movable_items:
                    i.moveBy(xoffset, 0)
                
            
    n2i[root_node].max_r = max_r
    return max_r
def render_circular(root_node, n2i, rot_step):
    max_r = 0.0
    for node in root_node.traverse('preorder', is_leaf_fn=_leaf):
        item = n2i[node]
        w = sum(item.widths[1:5])
        h = item.effective_height

        parent_radius = n2i[node.up].radius if node.up else item.xoff
        angle = rot_step if _leaf(node) else item.angle_span

        if hasattr(item, "radius"):
            r = item.radius
            xoffset = 0
        else:
            r, xoffset = get_min_radius(w, h, angle, parent_radius + item.widths[0])
            item.radius = r
            node.add_features(rad=item.radius)

        #if xoffset: # DEBUG ONLY. IF Scale is correct, this should not be printed
        #    print "Offset detected in node", xoffset

        rotate_and_displace(item.content, item.rotation, h, parent_radius)
        
        max_r = max(max_r, r)

        if not _leaf(node) and len(node.children) > 1:
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]
            # Vertical arc Line
            rot_end = n2i[node.children[-1]].rotation
            rot_start = n2i[node.children[0]].rotation
            rot_span = abs(rot_end - rot_start)
            C = item.vt_line
            C.setParentItem(item)
            path = QtGui.QPainterPath()
            # Counter clock wise
            start = r - node.img_style["vt_line_width"]/2
            path.arcMoveTo(-start, -start, start * 2, start * 2, 360 - rot_start - rot_span)
            path.arcTo(-start, -start, start * 2, start * 2, 360 - rot_start - rot_span, rot_span)
            # Faces
            C.setPath(path)
            item.static_items.append(C)


        if hasattr(item, "content"):

            # If applies, it sets the length of the extra branch length
            if item.extra_branch_line:
                xtra =  item.extra_branch_line.line().dx()
                if xtra > 0:
                    xtra = xoffset + xtra
                else:
                    xtra = xoffset
                item.extra_branch_line.setLine(item.branch_length, item.center,\
                                               item.branch_length + xtra , item.center)
                item.nodeRegion.setWidth(item.nodeRegion.width()+xtra)

            # And moves elements 
            if xoffset:
                for i in item.movable_items:
                    i.moveBy(xoffset, 0)
                
            
    n2i[root_node].max_r = max_r
    return max_r
Example #14
0
def init_node_dimensions(node, item, faceblock, img):
    """Calculates width and height of all different subparts and faces
    of a given node. Branch lengths are not taken into account, so some
    dimensions must be adjusted after setting a valid scale.
    """
    
    min_separation = img.min_leaf_separation

    if _leaf(node):
        aligned_height = faceblock["aligned"].h
        aligned_width = faceblock["aligned"].w
    else:
        aligned_height = 0
        aligned_width = 0
        
    ndist =  1.0 if img.force_topology else node.dist
    item.branch_length = (ndist * img._scale) if img._scale else 0
    ## Calculate dimensions of the different node regions
    ##
    ##
    ##                                |  
    ##                                |        ------ 
    ##          b-top       --------- |        |    | 
    ## xoff-------------- O |b-right| |        |alg | 
    ##          b-bottom    --------- |        |    | 
    ##                                |        ------ 
    ##                                |       
    ##                                        
    ##      0     1       2     3     4           5   
    ##

    item.xoff = 0.0
    # widths
    w1 = max(faceblock["branch-bottom"].w, faceblock["branch-top"].w)
    w0 = item.branch_length - w1 if item.branch_length > w1 else 0
    w2 = node.img_style["size"]
    w3 = faceblock["branch-right"].w
    w4 = node.img_style["vt_line_width"] if not _leaf(node) and len(node.children) > 1 else 0.0
    w5 = 0
    # heights
    h0 = node.img_style["hz_line_width"]
    h1 = node.img_style["hz_line_width"] + faceblock["branch-top"].h + faceblock["branch-bottom"].h
    h2 = node.img_style["size"]
    h3 = faceblock["branch-right"].h
    h4 = 0
    h5 = aligned_height
    
    # ignore face heights if requested
    if img.mode == "c" and img.allow_face_overlap:
        h1, h3, h5 = 0, 0, 0
    
    item.heights = [h0, h1, h2, h3, h4, h5]
    item.widths = [w0, w1, w2, w3, w4, w5]

    # Calculate total node size
    total_w = sum([w0, w1, w2, w3, w4, item.xoff]) # do not count aligned faces
	
    if img.mode == "c":
        max_h = max(item.heights[:4] + [min_separation])
    elif img.mode == "r":
        max_h = max(item.heights + [min_separation])

    max_h += img.branch_vertical_margin
        
    # correct possible unbalanced block in branch faces
    h_imbalance = abs(faceblock["branch-top"].h - faceblock["branch-bottom"].h)
    if h_imbalance + h1 > max_h:
        max_h += h_imbalance
        
    item.facesRegion.setRect(0, 0, w3, max_h)
    item.nodeRegion.setRect(0, 0, total_w, max_h)
    item.fullRegion.setRect(0, 0, total_w, max_h)
Example #15
0
def render(root_node, img, hide_root=False):
    '''main render function. hide_root option is used when render
    trees as Faces

    '''
    mode = img.mode
    orientation = img.orientation

    arc_span = img.arc_span

    layout_fn = img._layout_handler

    parent = _TreeItem()
    n2i = parent.n2i # node to items
    n2f = parent.n2f # node to faces

    parent.bg_layer =  _EmptyItem(parent)
    parent.tree_layer = _EmptyItem(parent)
    parent.float_layer = _EmptyItem(parent)
    parent.float_behind_layer = _EmptyItem(parent)

    TREE_LAYERS = [parent.bg_layer, parent.tree_layer,
                   parent.float_layer, parent.float_behind_layer]

    parent.bg_layer.setZValue(0)
    parent.tree_layer.setZValue(2)

    parent.float_behind_layer.setZValue(1)
    parent.float_layer.setZValue(3)

    # This could be used to handle aligned faces in internal
    # nodes.
    virtual_leaves = 0

    if img.show_branch_length:
        bl_face = faces.AttrFace("dist", fsize=8, ftype="Arial", fgcolor="black", formatter = "%0.3g")
    if img.show_branch_support:
        su_face = faces.AttrFace("support", fsize=8, ftype="Arial", fgcolor="darkred", formatter = "%0.3g")
    if img.show_leaf_name:
        na_face = faces.AttrFace("name", fsize=10, ftype="Arial", fgcolor="black")
    
    for n in root_node.traverse(is_leaf_fn=_leaf):
        set_style(n, layout_fn)

        if img.show_branch_length:
            faces.add_face_to_node(bl_face, n, 0, position="branch-top")

        if not _leaf(n) and img.show_branch_support:
            faces.add_face_to_node(su_face, n, 0, position="branch-bottom")

        if _leaf(n) and n.name and img.show_leaf_name:
            faces.add_face_to_node(na_face, n, 0, position="branch-right")

        if _leaf(n):# or len(n.img_style["_faces"]["aligned"]):
            virtual_leaves += 1

        update_node_faces(n, n2f, img)

    rot_step = float(arc_span) / virtual_leaves
    #rot_step = float(arc_span) / len([n for n in root_node.traverse() if _leaf(n)])

    # Calculate optimal branch length
    if img._scale is not None:
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
    elif img.scale is None:
        # create items and calculate node dimensions skipping branch lengths
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
        if mode == 'r':
            if img.optimal_scale_level == "full":
                scales = [(i.widths[1]/n.dist) for n,i in n2i.iteritems() if n.dist]
                img._scale = max(scales) if scales else 0.0
            else:
                farthest, dist = root_node.get_farthest_leaf(topology_only=img.force_topology)
                img._scale = img.tree_width / dist if dist else 0.0
            update_branch_lengths(root_node, n2i, n2f, img)
        else:
            img._scale = crender.calculate_optimal_scale(root_node, n2i, rot_step, img)
            #print "OPTIMAL circular scale", img._scale
            update_branch_lengths(root_node, n2i, n2f, img)
            init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
    else:
        # create items and calculate node dimensions CONSIDERING branch lengths
        img._scale = img.scale
        init_items(root_node, parent, n2i, n2f, img, rot_step, hide_root)
        
    #print "USING scale", img._scale
    # Draw node content
    for node in root_node.traverse(is_leaf_fn=_leaf):
        if node is not root_node or not hide_root:
            render_node_content(node, n2i, n2f, img)

    # Adjust content to rect or circular layout
    mainRect = parent.rect()

    if mode == "c":
        tree_radius = crender.render_circular(root_node, n2i, rot_step)
        mainRect.adjust(-tree_radius, -tree_radius, tree_radius, tree_radius)
    else:
        iwidth = n2i[root_node].fullRegion.width()
        iheight = n2i[root_node].fullRegion.height()
        mainRect.adjust(0, 0, iwidth, iheight)
        tree_radius = iwidth

    # Add extra layers: aligned faces, floating faces, node
    # backgrounds, etc. The order by which the following methods are
    # called IS IMPORTANT
    render_floatings(n2i, n2f, img, parent.float_layer, parent.float_behind_layer)

    aligned_region_width = render_aligned_faces(img, mainRect, parent.tree_layer, n2i, n2f)

    render_backgrounds(img, mainRect, parent.bg_layer, n2i, n2f)

    # rotate if necessary in circular images. flip and adjust if mirror orientation. 
    adjust_faces_to_tranformations(img, mainRect, n2i, n2f, TREE_LAYERS)

    # Rotate main image if necessary
    parent.setRect(mainRect)
    parent.setPen(QtGui.QPen(QtCore.Qt.NoPen))

    if img.rotation:
        rect = parent.boundingRect()
        x =  rect.x() + (rect.width()/2.0)
        y =  rect.y() +  (rect.height()/2.0)
        parent.setTransform(QtGui.QTransform().translate(x, y).rotate(img.rotation).translate(-x, -y))

    # Creates the main tree item that will act as frame for the whole image
    frame = QtGui.QGraphicsRectItem()
    parent.setParentItem(frame)
    mainRect = parent.mapToScene(mainRect).boundingRect()

    mainRect.adjust(-img.margin_left, -img.margin_top, \
                         img.margin_right, img.margin_bottom)

    # Fix negative coordinates, so main item always starts at 0,0
    topleft  = mainRect.topLeft()
    _x = abs(topleft.x()) if topleft.x() < 0 else 0
    _y = abs(topleft.y()) if topleft.y() < 0 else 0
    if _x or _y:
        parent.moveBy(_x, _y)
        mainRect.adjust(_x, _y, _x, _y)
        
    # Add extra components and adjust mainRect to them
    add_legend(img, mainRect, frame)
    add_title(img, mainRect, frame)
    add_scale(img, mainRect, frame)
    frame.setRect(mainRect)

    # Draws a border around the tree
    if not img.show_border:
        frame.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    else:
        frame.setPen(QtGui.QPen(QtGui.QColor("black")))
    
    return frame, n2i, n2f
Example #16
0
def init_node_dimensions(node, item, faceblock, img):
    """Calculates width and height of all different subparts and faces
    of a given node. Branch lengths are not taken into account, so some
    dimensions must be adjusted after setting a valid scale.
    """
    
    min_separation = img.min_leaf_separation

    if _leaf(node):
        aligned_height = faceblock["aligned"].h
        aligned_width = faceblock["aligned"].w
    else:
        aligned_height = 0
        aligned_width = 0
        
    ndist =  1.0 if img.force_topology else node.dist
    item.branch_length = (ndist * img._scale) if img._scale else 0
    ## Calculate dimensions of the different node regions
    ##
    ##
    ##                                |  
    ##                                |        ------ 
    ##          b-top       --------- |        |    | 
    ## xoff-------------- O |b-right| |        |alg | 
    ##          b-bottom    --------- |        |    | 
    ##                                |        ------ 
    ##                                |       
    ##                                        
    ##      0     1       2     3     4           5   
    ##

    item.xoff = 0.0
    # widths
    w1 = max(faceblock["branch-bottom"].w, faceblock["branch-top"].w)
    w0 = item.branch_length - w1 if item.branch_length > w1 else 0
    w2 = node.img_style["size"]
    w3 = faceblock["branch-right"].w
    w4 = node.img_style["vt_line_width"] if not _leaf(node) and len(node.children) > 1 else 0.0
    w5 = 0
    # heights
    h0 = node.img_style["hz_line_width"]
    h1 = node.img_style["hz_line_width"] + faceblock["branch-top"].h + faceblock["branch-bottom"].h
    h2 = node.img_style["size"]
    h3 = faceblock["branch-right"].h
    h4 = 0
    h5 = aligned_height

    # This fixes the problem of miss-aligned branches in ultrametric trees. If
    # there is nothing between the hz line and the vt line, then I prevent
    # vt_line_width to add extra width to the node, so node distances are
    # preserved in the img.
    if w2 == 0 and w3 == 0:
        w4 = 0
    
    # ignore face heights if requested
    if img.mode == "c" and img.allow_face_overlap:
        h1, h3, h5 = 0, 0, 0
    
    item.heights = [h0, h1, h2, h3, h4, h5]
    item.widths = [w0, w1, w2, w3, w4, w5]

    # Calculate total node size
    total_w = sum([w0, w1, w2, w3, w4, item.xoff]) # do not count aligned faces
	
    if img.mode == "c":
        max_h = max(item.heights[:4] + [min_separation])
    elif img.mode == "r":
        max_h = max(item.heights + [min_separation])

    max_h += img.branch_vertical_margin
        
    # correct possible unbalanced block in branch faces
    h_imbalance = abs(faceblock["branch-top"].h - faceblock["branch-bottom"].h)
    if h_imbalance + h1 > max_h:
        max_h += h_imbalance
        
    item.facesRegion.setRect(0, 0, w3, max_h)
    item.nodeRegion.setRect(0, 0, total_w, max_h)
    item.fullRegion.setRect(0, 0, total_w, max_h)
Example #17
0
def render_aligned_faces(img, mainRect, parent, n2i, n2f):
    # Prepares and renders aligned face headers. Used to later
    # place aligned faces
    aligned_faces = [ [node, fb["aligned"]] for node, fb in n2f.iteritems()\
                          if fb["aligned"].column2faces and _leaf(node)]

    # If no aligned faces, just return an offset of 0 pixels
    if not aligned_faces:
        return 0

    # Load header and footer
    if img.mode == "r":
        tree_end_x = mainRect.width()

        fb_head = _FaceGroupItem(img.aligned_header, None)
        fb_head.setParentItem(parent)
        fb_foot = _FaceGroupItem(img.aligned_foot, None)
        fb_foot.setParentItem(parent)
        surroundings = [[None,fb_foot], [None, fb_head]]
        mainRect.adjust(0, -fb_head.h, 0, fb_foot.h)
    else:
        tree_end_x = mainRect.width()/2.0
        surroundings = []

    # Place aligned faces and calculates the max size of each
    # column (needed to place column headers)
    c2max_w = {}
    maxh = 0
    maxh_node = None
    for node, fb in aligned_faces + surroundings:
        if fb.h > maxh:
            maxh = fb.h
            maxh_node = node
        for c, w in fb.c2max_w.iteritems():
            c2max_w[c] = max(w, c2max_w.get(c,0))
    extra_width = sum(c2max_w.values())

    # If rect mode, render header and footer
    if img.mode == "r":
        if img.draw_aligned_faces_as_table:
            fb_head.setup_grid(c2max_w)
            fb_foot.setup_grid(c2max_w)

        fb_head.render()
        fb_head.setPos(tree_end_x, mainRect.top())
        fb_foot.render()
        fb_foot.setPos(tree_end_x, mainRect.bottom()-fb_foot.h)
        if img.orientation == 1:
            fb_head.flip_hz()
            fb_foot.flip_hz()

    # if no scale provided in circular mode, optimal scale is expected
    # to provide the correct ending point to start drawing aligned
    # faces.
    elif img.mode == "c" and (img.scale or img._scale == 0) and not img.allow_face_overlap:
        angle = n2i[maxh_node].angle_span
        rad, off = crender.get_min_radius(1, maxh, angle, tree_end_x)
        extra_width += rad - tree_end_x
        tree_end_x = rad

    # Place aligned faces
    for node, fb in aligned_faces:
        item = n2i[node]
        item.mapped_items.append(fb)
        if img.draw_aligned_faces_as_table:
            if img.aligned_table_style == 0:
                fb.setup_grid(c2max_w, as_grid=True)
            elif img.aligned_table_style == 1:
                fb.setup_grid(c2max_w, as_grid=False)

        fb.render()
        fb.setParentItem(item.content)
        if img.mode == "c":
            if node.up in n2i:
                x = tree_end_x - n2i[node.up].radius
            else:
                x = tree_end_x
            #fb.moveBy(tree_end_x, 0)
        elif img.mode == "r":
            x = item.mapFromScene(tree_end_x, 0).x()

        fb.setPos(x, item.center-(fb.h/2.0))

        if img.draw_guiding_lines and _leaf(node):
            # -1 is to connect the two lines, otherwise there is a pixel in between
            guide_line = _LineItem(item.nodeRegion.width()-1, item.center, x, item.center)
            pen = QtGui.QPen()
            set_pen_style(pen, img.guiding_lines_type)
            pen.setColor(QtGui.QColor(img.guiding_lines_color))
            pen.setCapStyle(QtCore.Qt.FlatCap)
            pen.setWidth(node.img_style["hz_line_width"])
            guide_line.setPen(pen)
            guide_line.setParentItem(item.content)

    if img.mode == "c":
        mainRect.adjust(-extra_width, -extra_width, extra_width, extra_width)
    else:
        mainRect.adjust(0, 0, extra_width, 0)
    return extra_width
Example #18
0
def calculate_optimal_scale(root_node, n2i, rot_step, img):
    """ Note: Seems to be fast. 0.5s from a tree of 10.000 leaves"""

    n2minradius = {}
    n2sumdist = {}
    n2sumwidth = {}
    visited_nodes = []
    # Calcula la posicion minima de los elementos (con scale=0, es
    # decir, sin tener en cuenta branch lengths.
    for node in root_node.traverse('preorder', is_leaf_fn=_leaf):
        visited_nodes.append(node)
        ndist = node.dist if not img.force_topology else 1.0
        item = n2i[node]
        # Uses size of all node parts, except branch length
        w = sum(item.widths[1:5])
        h = item.effective_height
        parent_radius = n2minradius.get(node.up, 0)
        angle = rot_step if _leaf(node) else item.angle_span

        r, xoffset = get_min_radius(w, h, angle, parent_radius)
        n2minradius[node] = r
        n2sumdist[node] = n2sumdist.get(node.up, 0) + ndist
        # versed sine: the little extra line needed to complete the
        # radius.
        #vs = r - (parent_radius + xoffset + w)
        n2sumwidth[node] = n2sumwidth.get(node.up, 0) + sum(
            item.widths[2:5])  #+ vs

    root_opening = 0.0
    most_distant = max(n2sumdist.values())
    if most_distant == 0: return 0.0

    best_scale = None
    for node in visited_nodes:
        item = n2i[node]
        ndist = node.dist if not img.force_topology else 1.0
        if best_scale is None:
            best_scale = (n2minradius[node] -
                          n2sumwidth[node]) / ndist if ndist else 0.0
        else:
            #Whats the expected radius of this node?
            current_rad = n2sumdist[node] * best_scale + (n2sumwidth[node] +
                                                          root_opening)

            # If still too small, it means we need to increase scale.
            if current_rad < n2minradius[node]:
                # This is a simplification of the real ecuacion needed
                # to calculate the best scale. Given that I'm not
                # taking into account the versed sine of each parent
                # node, the equation is actually very simple.
                if img.root_opening_factor:
                    best_scale = (n2minradius[node] - (n2sumwidth[node])) / (
                        n2sumdist[node] +
                        (most_distant * img.root_opening_factor))
                    root_opening = most_distant * best_scale * img.root_opening_factor
                else:
                    best_scale = (n2minradius[node] - (n2sumwidth[node]) +
                                  root_opening) / n2sumdist[node]
                #print "OOps adjusting scale", ndist, best_scale, n2minradius[node], current_rad, item.heights[5], node.name

            # If the width of branch top/bottom faces is not covered,
            # we can also increase the scale to adjust it. This may
            # produce huge scales, so let's keep it optional
            if img.optimal_scale_level == "full" and \
               item.widths[1] > ndist * best_scale:
                best_scale = item.widths[1] / ndist
                #print "OOps adjusting scale because  branch-faces", ndist, best_scale, item.widths[1]

    # Adjust scale for aligned faces
    if not img.allow_face_overlap:
        aligned_h = [(n2i[node].heights[5], node) for node in visited_nodes]
        aligned_h.sort(reverse=True)
        maxh, maxh_node = aligned_h[0]
        angle = n2i[maxh_node].angle_span
        rad, off = get_min_radius(1, maxh, angle, 0.0001)
        min_alg_scale = None
        for node in visited_nodes:
            if n2i[node].heights[5]:
                new_scale = (
                    rad - (n2sumwidth[node] + root_opening)) / n2sumdist[node]
                min_alg_scale = min(
                    new_scale,
                    min_alg_scale) if min_alg_scale is not None else new_scale
        if min_alg_scale > best_scale:
            best_scale = min_alg_scale

    if root_opening:
        n2i[root_node].nodeRegion.adjust(root_opening, 0, root_opening, 0)
        n2i[root_node].fullRegion.adjust(root_opening, 0, root_opening, 0)
        n2i[root_node].xoff = root_opening
        #n2i[root_node].widths[0] += root_opening

    #for node in visited_nodes:
    #    item = n2i[node]
    #    h = item.effective_height
    #    a = n2sumdist[node] * best_scale + n2sumwidth.get(node)
    #    b = h/2
    #    item.radius = math.sqrt(a**2 + b**2)
    #print "root opening", root_opening
    #best_scale = max(best_scale, min_scale)
    return best_scale
Example #19
0
def set_node_size(node, n2i, n2f, img):
    scale = img._scale
    min_separation = img.min_leaf_separation

    item = n2i[node]
    if img.force_topology:
        branch_length = item.branch_length = 25
    else:
        branch_length = item.branch_length = float(node.dist * scale)

    # Organize faces by groups
    #faceblock = update_node_faces(node, n2f, img)
    faceblock = n2f[node]
    aligned_height = 0
    if _leaf(node):
        if img.mode == "r":
            aligned_height = faceblock["aligned"].h
        elif img.mode == "c":
            # aligned faces in circular mode are adjusted afterwords. The
            # min radius of the largest aligned faces will be calculated.
            pass

    # Total height required by the node. I cannot sum up the height of
    # all elements, since the position of some of them are forced to
    # be on top or at the bottom of branches. This fact can produce
    # and unbalanced nodeRegion center. Here, I only need to know
    # about the imbalance size to correct node height. The center will
    # be calculated later according to the parent position.
    top_half_h = ( (node.img_style["size"]/2.0) +
                       node.img_style["hz_line_width"]/2.0 +
                       faceblock["branch-top"].h )

    bottom_half_h =( (node.img_style["size"]/2.0) +
                       node.img_style["hz_line_width"]/2.0 +
                       faceblock["branch-bottom"].h )

    h1 = top_half_h + bottom_half_h
    h2 = max(faceblock["branch-right"].h, \
                 aligned_height, \
                min_separation )
    h = max(h1, h2)
    imbalance = abs(top_half_h - bottom_half_h)
    if imbalance > h2/2.0:
        h += imbalance - (h2/2.0)

    # This adds a vertical margin around the node elements
    h += img.branch_vertical_margin

    # Total width required by the node
    w = sum([max(branch_length + node.img_style["size"],
                 faceblock["branch-top"].w + node.img_style["size"],
                 faceblock["branch-bottom"].w + node.img_style["size"],
                 ),
             faceblock["branch-right"].w]
            )

    # This breaks ultrametric tree visualization
    #w += node.img_style["vt_line_width"]
    
    # rightside faces region
    item.facesRegion.setRect(0, 0, faceblock["branch-right"].w, h)

    # Node region
    item.nodeRegion.setRect(0, 0, w, h)

    # This is the node total region covered by the node
    item.fullRegion.setRect(0, 0, w, h)
def calculate_optimal_scale(root_node, n2i, rot_step, img):
    """ Note: Seems to be fast. 0.5s from a tree of 10.000 leaves""" 
    
    n2minradius = {}
    n2sumdist = {}
    n2sumwidth = {}
    visited_nodes = []
    # Calcula la posicion minima de los elementos (con scale=0, es
    # decir, sin tener en cuenta branch lengths.
    for node in root_node.traverse('preorder', is_leaf_fn=_leaf):
        visited_nodes.append(node)
        ndist = node.dist if not img.force_topology else 1.0
        item = n2i[node]
        # Uses size of all node parts, except branch length
        w = sum(item.widths[1:5])
        h = item.effective_height
        parent_radius = n2minradius.get(node.up, 0)
        angle = rot_step if _leaf(node) else item.angle_span
            
        r, xoffset = get_min_radius(w, h, angle, parent_radius)
        n2minradius[node] = r 
        n2sumdist[node] = n2sumdist.get(node.up, 0) + ndist 
        # versed sine: the little extra line needed to complete the
        # radius.
        #vs = r - (parent_radius + xoffset + w)
        n2sumwidth[node] = n2sumwidth.get(node.up, 0) + sum(item.widths[2:5]) #+ vs

    root_opening = 0.0
    most_distant = max(n2sumdist.values())
    if most_distant == 0: return 0.0
    
    best_scale = None
    for node in visited_nodes:
        item = n2i[node]
        ndist = node.dist if not img.force_topology else 1.0
        if best_scale is None:
            best_scale = (n2minradius[node] - n2sumwidth[node]) / ndist if ndist else 0.0
        else:
            #Whats the expected radius of this node?
            current_rad = n2sumdist[node] * best_scale + (n2sumwidth[node] + root_opening)
                    
            # If still too small, it means we need to increase scale.
            if current_rad < n2minradius[node]:
                # This is a simplification of the real ecuacion needed
                # to calculate the best scale. Given that I'm not
                # taking into account the versed sine of each parent
                # node, the equation is actually very simple.
                if img.root_opening_factor: 
                    best_scale = (n2minradius[node] - (n2sumwidth[node])) / (n2sumdist[node] + (most_distant * img.root_opening_factor))
                    root_opening = most_distant * best_scale * img.root_opening_factor
                else:
                    best_scale = (n2minradius[node] - (n2sumwidth[node]) + root_opening) / n2sumdist[node]
                #print "OOps adjusting scale", ndist, best_scale, n2minradius[node], current_rad, item.heights[5], node.name

            # If the width of branch top/bottom faces is not covered,
            # we can also increase the scale to adjust it. This may
            # produce huge scales, so let's keep it optional
            if img.optimal_scale_level == "full" and \
               item.widths[1] > ndist * best_scale:
                best_scale = item.widths[1] / ndist
                #print "OOps adjusting scale because  branch-faces", ndist, best_scale, item.widths[1]

    # Adjust scale for aligned faces
    if not img.allow_face_overlap:
        aligned_h = [(n2i[node].heights[5], node) for node in visited_nodes]
        aligned_h.sort(reverse=True)
        maxh, maxh_node = aligned_h[0]
        angle = n2i[maxh_node].angle_span
        rad, off = get_min_radius(1, maxh, angle, 0.0001)
        min_alg_scale = None
        for node in visited_nodes:
            if n2i[node].heights[5]:
                new_scale = (rad - (n2sumwidth[node] + root_opening)) / n2sumdist[node]
                min_alg_scale = min(new_scale, min_alg_scale) if min_alg_scale is not None else new_scale
        if min_alg_scale >  best_scale:
            best_scale = min_alg_scale
            
    if root_opening:
        n2i[root_node].nodeRegion.adjust(root_opening, 0, root_opening, 0)
        n2i[root_node].fullRegion.adjust(root_opening, 0, root_opening, 0)
        n2i[root_node].xoff = root_opening
        #n2i[root_node].widths[0] += root_opening

    #for node in visited_nodes:
    #    item = n2i[node]
    #    h = item.effective_height
    #    a = n2sumdist[node] * best_scale + n2sumwidth.get(node) 
    #    b = h/2
    #    item.radius = math.sqrt(a**2 + b**2)
    #print "root opening", root_opening
    #best_scale = max(best_scale, min_scale)
    return best_scale
Example #21
0
def render_circular(root_node, n2i, rot_step):
    max_r = 0.0
    for node in root_node.traverse('preorder', is_leaf_fn=_leaf):
        item = n2i[node]
        w = sum(item.widths[1:5])
        h = item.effective_height

        parent_radius = n2i[
            node.up].radius if node.up and node.up in n2i else item.xoff
        angle = rot_step if _leaf(node) else item.angle_span

        if hasattr(item, "radius"):
            r = item.radius
            xoffset = 0
        else:
            r, xoffset = get_min_radius(w, h, angle,
                                        parent_radius + item.widths[0])
            item.radius = r
            node.add_features(rad=item.radius)

        #if xoffset: # DEBUG ONLY. IF Scale is correct, this should not be printed
        #    print "Offset detected in node", xoffset

        rotate_and_displace(item.content, item.rotation, h, parent_radius)

        max_r = max(max_r, r)

        if not _leaf(node) and len(node.children) > 1:
            first_c = n2i[node.children[0]]
            last_c = n2i[node.children[-1]]
            # Vertical arc Line
            rot_end = n2i[node.children[-1]].rotation
            rot_start = n2i[node.children[0]].rotation
            rot_span = abs(rot_end - rot_start)
            C = item.vt_line
            C.setParentItem(item)
            path = QtGui.QPainterPath()
            # Counter clock wise
            start = r - node.img_style["vt_line_width"] / 2
            path.arcMoveTo(-start, -start, start * 2, start * 2,
                           360 - rot_start - rot_span)
            path.arcTo(-start, -start, start * 2, start * 2,
                       360 - rot_start - rot_span, rot_span)
            # Faces
            C.setPath(path)
            item.static_items.append(C)

        if hasattr(item, "content"):

            # If applies, it sets the length of the extra branch length
            if item.extra_branch_line:
                xtra = item.extra_branch_line.line().dx()
                if xtra > 0:
                    xtra = xoffset + xtra
                else:
                    xtra = xoffset
                item.extra_branch_line.setLine(item.branch_length, item.center,\
                                               item.branch_length + xtra , item.center)
                item.nodeRegion.setWidth(item.nodeRegion.width() + xtra)

            # And moves elements
            if xoffset:
                for i in item.movable_items:
                    i.moveBy(xoffset, 0)

    n2i[root_node].max_r = max_r
    return max_r
Example #22
0
def render_node_content(node, n2i, n2f, img):
    style = node.img_style
    item = n2i[node]
    item.content = _EmptyItem(item)

    nodeR = item.nodeRegion
    facesR = item.facesRegion
    center = item.center
    branch_length = item.branch_length

    # Node points
    ball_size = style["size"]


    vlw = style["vt_line_width"] if not _leaf(node) and len(node.children) > 1 else 0.0

    # face_start_x = nodeR.width() - facesR.width() - vlw
    face_start_x = max(0, nodeR.width() - facesR.width() - vlw)
    ball_start_x = face_start_x - ball_size 
    
    if ball_size:
        if node.img_style["shape"] == "sphere":
            node_ball = _SphereItem(node)
        elif node.img_style["shape"] == "circle":
            node_ball = _CircleItem(node)
        elif node.img_style["shape"] == "square":
            node_ball = _RectItem(node)

        node_ball.setPos(ball_start_x, center-(ball_size/2.0))

        #from qt4_gui import _BasicNodeActions
        #node_ball.delegate = _BasicNodeActions()
        #node_ball.setAcceptsHoverEvents(True)
        #node_ball.setCursor(QtCore.Qt.PointingHandCursor)

    else:
        node_ball = None

    # Branch line to parent
    pen = QtGui.QPen()
    set_pen_style(pen, style["hz_line_type"])
    pen.setColor(QtGui.QColor(style["hz_line_color"]))
    pen.setWidth(style["hz_line_width"])
    pen.setCapStyle(QtCore.Qt.FlatCap)
    #pen.setCapStyle(QtCore.Qt.RoundCap)
    #pen.setCapStyle(QtCore.Qt.SquareCap)
    #pen.setJoinStyle(QtCore.Qt.RoundJoin)
    hz_line = _LineItem()
    hz_line = _NodeLineItem(node)
    hz_line.setPen(pen)

    join_fix = 0
    if img.mode == "c" and node.up and node.up.img_style["vt_line_width"]:
        join_fix = node.up.img_style["vt_line_width"] 
        # fix_join_line = _LineItem()
        # fix_join_line = _NodeLineItem(node)
        # parent_style = node.up.img_style
        # pen = QtGui.QPen()
        # pen.setColor(QtGui.QColor(parent_style["vt_line_color"]))
        # pen.setWidth(parent_style["hz_line_width"])
        # pen.setCapStyle(QtCore.Qt.FlatCap)
        # fix_join_line.setPen(pen)        
        # fix_join_line.setLine(-join_fix, center, join_fix, center)
        # fix_join_line.setParentItem(item.content)

    hz_line.setLine(-join_fix, center, branch_length, center)

    if img.complete_branch_lines_when_necessary:
        extra_line = _LineItem(branch_length, center, ball_start_x, center)
        pen = QtGui.QPen()
        item.extra_branch_line = extra_line
        set_pen_style(pen, img.extra_branch_line_type)
        pen.setColor(QtGui.QColor(img.extra_branch_line_color))
        pen.setCapStyle(QtCore.Qt.FlatCap)
        pen.setWidth(style["hz_line_width"])
        extra_line.setPen(pen)
    else:
        extra_line = None

    # Attach branch-right faces to child
    fblock_r = n2f[node]["branch-right"]
    fblock_r.render()
    fblock_r.setPos(face_start_x, center-fblock_r.h/2.0)

    # Attach branch-bottom faces to child
    fblock_b = n2f[node]["branch-bottom"]
    fblock_b.render()
    fblock_b.setPos(item.widths[0], center + style["hz_line_width"]/2.0)

    # Attach branch-top faces to child
    fblock_t = n2f[node]["branch-top"]
    fblock_t.render()
    fblock_t.setPos(item.widths[0], center - fblock_t.h - style["hz_line_width"]/2.0)

    # Vertical line
    if not _leaf(node):
        if img.mode == "c":
            vt_line = QtGui.QGraphicsPathItem()

        elif img.mode == "r":
            vt_line = _LineItem(item)
            first_child = node.children[0]
            last_child = node.children[-1]
            first_child_part = n2i[node.children[0]]
            last_child_part = n2i[node.children[-1]]
            c1 = first_child_part.start_y + first_child_part.center
            c2 = last_child_part.start_y + last_child_part.center
            fx = nodeR.width() - (vlw/2.0)
            if first_child.img_style["hz_line_width"] > 0:
                c1 -= (first_child.img_style["hz_line_width"] / 2.0)
            if last_child.img_style["hz_line_width"] > 0:
                c2 += (last_child.img_style["hz_line_width"] / 2.0)
            vt_line.setLine(fx, c1, fx, c2)

        pen = QtGui.QPen()
        set_pen_style(pen, style["vt_line_type"])
        pen.setColor(QtGui.QColor(style["vt_line_color"]))
        pen.setWidth(style["vt_line_width"])
        pen.setCapStyle(QtCore.Qt.FlatCap)
        #pen.setCapStyle(QtCore.Qt.RoundCap)
        #pen.setCapStyle(QtCore.Qt.SquareCap)
        vt_line.setPen(pen)
        item.vt_line = vt_line
    else:
        vt_line = None

    item.bg = QtGui.QGraphicsItemGroup()
    item.movable_items = [] #QtGui.QGraphicsItemGroup()
    item.static_items = [] #QtGui.QGraphicsItemGroup()

    # Items fow which coordinates are exported in the image map
    item.mapped_items = [node_ball, fblock_r, fblock_b, fblock_t]


    for i in [vt_line, extra_line, hz_line]:
        if i:
            #item.static_items.addToGroup(i)
            item.static_items.append(i)
            i.setParentItem(item.content)
    for i in [node_ball, fblock_r, fblock_b, fblock_t]:
        if i:
            #item.movable_items.addToGroup(i)
            item.movable_items.append(i)
            i.setParentItem(item.content)
Example #23
0
def render_aligned_faces(img, mainRect, parent, n2i, n2f):
    # Prepares and renders aligned face headers. Used to later
    # place aligned faces
    aligned_faces = [ [node, fb["aligned"]] for node, fb in n2f.iteritems()\
                          if fb["aligned"].column2faces and _leaf(node)]

    # If no aligned faces, just return an offset of 0 pixels
    if not aligned_faces:
        return 0

    # Load header and footer
    if img.mode == "r":
        tree_end_x = mainRect.width()

        fb_head = _FaceGroupItem(img.aligned_header, None)
        fb_head.setParentItem(parent)
        fb_foot = _FaceGroupItem(img.aligned_foot, None)
        fb_foot.setParentItem(parent)
        surroundings = [[None,fb_foot], [None, fb_head]]
        mainRect.adjust(0, -fb_head.h, 0, fb_foot.h)
    else:
        tree_end_x = mainRect.width()/2.0
        surroundings = []

    # Place aligned faces and calculates the max size of each
    # column (needed to place column headers)
    c2max_w = {}
    maxh = 0
    maxh_node = None
    for node, fb in aligned_faces + surroundings:
        if fb.h > maxh:
            maxh = fb.h
            maxh_node = node
        for c, w in fb.c2max_w.iteritems():
            c2max_w[c] = max(w, c2max_w.get(c,0))
    extra_width = sum(c2max_w.values())

    # If rect mode, render header and footer
    if img.mode == "r":
        if img.draw_aligned_faces_as_table:
            fb_head.setup_grid(c2max_w)
            fb_foot.setup_grid(c2max_w)

        fb_head.render()
        fb_head.setPos(tree_end_x, mainRect.top())
        fb_foot.render()
        fb_foot.setPos(tree_end_x, mainRect.bottom()-fb_foot.h)
        if img.orientation == 1:
            fb_head.flip_hz()
            fb_foot.flip_hz()

    # if no scale provided in circular mode, optimal scale is expected
    # to provide the correct ending point to start drawing aligned
    # faces.
    elif img.mode == "c" and (img.scale or img._scale == 0) and not img.allow_face_overlap:
        angle = n2i[maxh_node].angle_span
        rad, off = crender.get_min_radius(1, maxh, angle, tree_end_x)
        extra_width += rad - tree_end_x
        tree_end_x = rad

    # Place aligned faces
    for node, fb in aligned_faces:
        item = n2i[node]
        item.mapped_items.append(fb)
        if img.draw_aligned_faces_as_table:
            if img.aligned_table_style == 0:
                fb.setup_grid(c2max_w, as_grid=True)
            elif img.aligned_table_style == 1:
                fb.setup_grid(c2max_w, as_grid=False)

        fb.render()
        fb.setParentItem(item.content)
        if img.mode == "c":
            if node.up in n2i:
                x = tree_end_x - n2i[node.up].radius
            else:
                x = tree_end_x
            #fb.moveBy(tree_end_x, 0)
        elif img.mode == "r":
            x = item.mapFromScene(tree_end_x, 0).x()

        fb.setPos(x, item.center-(fb.h/2.0))

        if img.draw_guiding_lines and _leaf(node):
            # -1 is to connect the two lines, otherwise there is a pixel in between
            guide_line = _LineItem(item.nodeRegion.width()-1, item.center, x, item.center)
            pen = QtGui.QPen()
            set_pen_style(pen, img.guiding_lines_type)
            pen.setColor(QtGui.QColor(img.guiding_lines_color))
            pen.setCapStyle(QtCore.Qt.FlatCap)
            pen.setWidth(node.img_style["hz_line_width"])
            guide_line.setPen(pen)
            guide_line.setParentItem(item.content)

    if img.mode == "c":
        mainRect.adjust(-extra_width, -extra_width, extra_width, extra_width)
    else:
        mainRect.adjust(0, 0, extra_width, 0)
    return extra_width
Example #24
0
def render(root_node, img, hide_root=False):
    mode = img.mode
    orientation = img.orientation 

    if not img.scale and img.tree_width:
        fnode, max_dist = root_node.get_farthest_leaf(topology_only=\
                                                          img.force_topology)
        if max_dist>0:
            img.scale =  img.tree_width / max_dist
        else:
            img.scale =  1

    scale = img.scale
    arc_span = img.arc_span 
    last_rotation = img.arc_start
    layout_fn = img._layout_handler
    
    parent = _TreeItem()
    n2i = parent.n2i # node to items
    n2f = parent.n2f # node to faces

    parent.bg_layer =  _EmptyItem(parent)
    parent.tree_layer = _EmptyItem(parent)
    parent.float_layer = _EmptyItem(parent)
    parent.float_behind_layer = _EmptyItem(parent)

    TREE_LAYERS = [parent.bg_layer, parent.tree_layer, parent.float_layer]

    parent.bg_layer.setZValue(0)
    parent.tree_layer.setZValue(2)

    parent.float_behind_layer.setZValue(1)
    parent.float_layer.setZValue(3)

    visited = set()
    to_visit = []
    to_visit.append(root_node)

    # This could be used to handle aligned faces in internal
    # nodes.
    virtual_leaves = 0
    
    if img.show_branch_length:
        bl_face = faces.AttrFace("dist", fsize=8, ftype="Arial", fgcolor="black", formatter = "%0.3g")
    if img.show_branch_support:
        su_face = faces.AttrFace("support", fsize=8, ftype="Arial", fgcolor="darkred", formatter = "%0.3g")
    if img.show_leaf_name:
        na_face = faces.AttrFace("name", fsize=10, ftype="Arial", fgcolor="black")

    for n in root_node.traverse():
        set_style(n, layout_fn)

        if img.show_branch_length:
            faces.add_face_to_node(bl_face, n, 0, position="branch-top")

        if not _leaf(n) and img.show_branch_support:
            faces.add_face_to_node(su_face, n, 0, position="branch-bottom")

        if _leaf(n) and img.show_leaf_name:
            faces.add_face_to_node(na_face, n, 0, position="branch-right")

        if _leaf(n):# or len(n.img_style["_faces"]["aligned"]):
            virtual_leaves += 1

    rot_step = float(arc_span) / virtual_leaves
    #rot_step = float(arc_span) / len([n for n in root_node.traverse() if _leaf(n)])

    # ::: Precalculate values :::
    depth = 1
    while to_visit:
        node = to_visit[-1]
        finished = True
        if node not in n2i:
            # Set style according to layout function
            item = n2i[node] = _NodeItem(node, parent.tree_layer)
            item.setZValue(depth)
            depth += 1 

            if node is root_node and hide_root:
                pass
            else:
                set_node_size(node, n2i, n2f, img)

        if not _leaf(node):
            # visit children starting from left most to right
            # most. Very important!! check all children[-1] and
            # children[0]
            for c in reversed(node.children):
                if c not in visited:
                    to_visit.append(c)
                    finished = False
            # :: pre-order code here ::
        if not finished:
            continue
        else:
            to_visit.pop(-1)
            visited.add(node)

        # :: Post-order visits. Leaves are already visited here ::
        if mode == "c": 
            if _leaf(node):
                crender.init_circular_leaf_item(node, n2i, n2f, last_rotation, rot_step)
                last_rotation += rot_step
            else:
                crender.init_circular_node_item(node, n2i, n2f)

        elif mode == "r": 
            if _leaf(node):
                rrender.init_rect_leaf_item(node, n2i, n2f)
            else:
                rrender.init_rect_node_item(node, n2i, n2f)

        if node is not root_node or not hide_root: 
            render_node_content(node, n2i, n2f, img)

    mainRect = parent.rect()

    if mode == "c":
        tree_radius = crender.render_circular(root_node, n2i, rot_step)
        mainRect.adjust( -tree_radius, -tree_radius, tree_radius, tree_radius)

    else:
        iwidth = n2i[root_node].fullRegion.width()
        iheight = n2i[root_node].fullRegion.height()
        mainRect.adjust(0, 0, iwidth, iheight)
        tree_radius = iwidth

    # The order by which the following methods IS IMPORTANT

    render_floatings(n2i, n2f, img, parent.float_layer, parent.float_behind_layer)

    aligned_region_width = render_aligned_faces(img, mainRect, parent.tree_layer, n2i, n2f)
   
    render_backgrounds(img, mainRect, parent.bg_layer, n2i, n2f)

    adjust_faces_to_tranformations(img, mainRect, n2i, n2f, TREE_LAYERS)

    parent.setRect(mainRect)
    parent.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    
    if img.rotation:
        rect = parent.boundingRect()
        x =  rect.x() + rect.width()/2
        y =  rect.y() +  rect.height()/2
        parent.setTransform(QtGui.QTransform().translate(x, y).rotate(img.rotation).translate(-x, -y))

    frame = QtGui.QGraphicsRectItem()
    parent.setParentItem(frame)
    mainRect = parent.mapToScene(mainRect).boundingRect()
   
    mainRect.adjust(-img.margin_left, -img.margin_top, \
                         img.margin_right, img.margin_bottom)

    add_legend(img, mainRect, frame)
    add_title(img, mainRect, frame)
    add_scale(img, mainRect, frame)
    frame.setRect(mainRect)

    # Draws a border around the tree
    if not img.show_border:
        frame.setPen(QtGui.QPen(QtCore.Qt.NoPen))
    else:
        frame.setPen(QtGui.QPen(QtGui.QColor("black")))

    return frame, n2i, n2f