cairo_draw.py 101 KB
Newer Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
                    b = u.vp["b"]
                    bs[0] = b.a
                else:
                    be = orig_state.project_level(l).get_edge_blocks()
                    emask = g.new_edge_property("bool")
                    for e in g.edges():
                        rs = be[e]
                        if rs[0] == tb[picked] and rs[1] == tb[picked]:
                            emask[e] = True
2010
2011
2012
                    u = GraphView(g, efilt=emask)
                    d = u.degree_property_map("total")
                    u = GraphView(u, vfilt=d.fa > 0)
2013
2014
2015
2016
2017
2018
2019
                    u.ep["be"] = orig_state.levels[0].get_edge_blocks()
                    u = Graph(u, prune=True)
                    be = u.ep["be"]
                    s = OverlapBlockState(u, b=be)
                    bs[0] = s.b.a.copy()

                nstate = NestedBlockState(u, bs=bs,
2020
                                          base_type=type(state.levels[0]),
2021
2022
                                          deg_corr=state.deg_corr)

2023
2024
2025
2026
2027
2028
                kwargs_ = kwargs_orig.copy()
                if "no_main" in kwargs_:
                    del kwargs_["no_main"]
                draw_hierarchy(nstate, beta=beta, vprops=vprops_orig,
                               eprops=eprops_orig, hvprops=hvprops_orig,
                               heprops=heprops_orig,
2029
                               subsample_edges=subsample_edges,
2030
2031
2032
                               deg_order=deg_order, empty_branches=False,
                               no_main=True, **kwargs_)

2033
        if key_id == ord('r'):
2034
2035
2036
2037
            if layout == "radial":
                x, y = ungroup_vector_property(pos, [0, 1])
                x.fa -= x.fa.mean()
                y.fa -= y.fa.mean()
2038
                angle = t_orig.new_vertex_property("double")
2039
2040
                angle.fa = (numpy.arctan2(y.fa, x.fa) + 2 * numpy.pi) % (2 * numpy.pi)
                tpos = radial_tree_layout(t_orig,
2041
                                          root=t_orig.vertex(t_orig.num_vertices(True) - 1),
2042
                                          rel_order=angle)
2043
                gg.copy_property(gg.own_property(tpos), pos)
2044

2045
2046
2047
2048
2049
2050
            update_cts(widget, gg, picked, pos, vprops, eprops)

            if widget.vertex_matrix is not None:
                widget.vertex_matrix.update()
            widget.picked = None
            widget.selected.fa = False
2051
2052
2053

            widget.fit_to_window()
            widget.regenerate_surface(reset=True)
2054
2055
            widget.queue_draw()

2056
    if "output" not in kwargs and not kwargs.get("inline", is_draw_inline):
2057
2058
2059
        kwargs["layout_callback"] = update_cts
        kwargs["key_press_callback"] = draw_branch

2060
2061
2062
    if "eorder" in kwargs:
        kwargs["eorder"] = eorder

2063
2064
2065
    vorder = kwargs.pop("vorder", None)
    if vorder is None:
        vorder = g.degree_property_map("total")
2066
    tvorder = u.own_property(tvorder)
2067
2068
    tvorder.fa[:g.num_vertices()] = vorder.fa

2069
2070
2071
2072
    for k, v in kwargs.items():
        if isinstance(v, PropertyMap) and v.get_graph().base is not u.base:
            kwargs[k] = u.own_property(v.copy())

2073
    pos = graph_draw(u, pos, vprops=t_vprops, eprops=t_eprops, vorder=tvorder,
2074
2075
                     **kwargs)

2076
    if isinstance(pos, PropertyMap):
2077
        t_orig.copy_property(pos, tpos, g=u)
2078
        pos = g.own_property(pos)
2079
    else:
2080
        t_orig.copy_property(pos[0], tpos, g=u)
2081
2082
        pos = (g.own_property(pos[0]),
               g.own_property(pos[1]))
2083
    return pos, t_orig, tpos
2084

2085

2086
def get_bip_hierachy_pos(state, aspect=1., node_weight=None):
2087

2088
    if state.levels[0].overlap:
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
        g = state.g
        ostate = state.levels[0]
        bv, bcin, bcout, bc = ostate.get_overlap_blocks()
        be = ostate.get_edge_blocks()

        n_r = zeros(ostate.B)
        b = g.new_vertex_property("int")
        for v in g.vertices():
            i = bc[v].a.argmax()
            b[v] = bv[v][i]
            n_r[b[v]] += 1

        orphans = [r for r in range(ostate.B) if n_r[r] == 0]

        for v in g.vertices():
            for r in orphans:
                b[v] = r

        orig_state = state
        state = state.copy()
        state.levels[0] = BlockState(g, b=b)

    g = state.g

    deg = g.degree_property_map("total")

    t, tb, order = get_hierarchy_tree(state)

2117
    root = t.vertex(t.num_vertices(True) - 1)
2118
2119
    if root.out_degree() > 2:
        clabel = is_bipartite(g, partition=True)[1].copy("int")
2120
        if state.levels[0].overlap:
2121
2122
            ostate = OverlapBlockState(g, b=clabel)
            ostate = orig_state.copy(clabel=clabel)
2123
            bc = ostate.propagate_clabel(len(state.levels) - 2)
2124
2125
        else:
            state = state.copy(clabel=clabel)
2126
            bc = state.propagate_clabel(len(state.levels) - 2)
2127

2128
        ps = list(root.out_neighbors())
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
        t.clear_vertex(root)

        p1 = t.add_vertex()
        p2 = t.add_vertex()

        t.add_edge(root, p1)
        t.add_edge(root, p2)
        for p in ps:
            if bc.a[tb[p]] == 0:
                t.add_edge(p2, p)
            else:
                t.add_edge(p1, p)

    w = t.new_vertex_property("double")
    for v in t.vertices():
        if v.in_degree() == 0:
            break
        if v.out_degree() == 0:
2147
            w[v] = 1 if node_weight is None else node_weight[v]
2148
        parent, = v.in_neighbors()
2149
2150
2151
2152
2153
2154
        w[parent] += w[v]

    pos = t.new_vertex_property("vector<double>")

    pos[root] = (0., 0.)

2155
    p1, p2 = root.out_neighbors()
2156

2157
2158
    if ((w[p1] == w[p2] and p1.out_degree() > p2.out_degree()) or
        w[p1] > w[p2]):
2159
2160
2161
        p1, p2 = p2, p1

    L = len(state.levels)
2162
2163
    pos[p1] = (-1 / L * .5 * aspect, 0)
    pos[p2] = (+1 / L * .5 * aspect, 0)
2164
2165
2166
2167
2168
2169

    for i, p in enumerate([p1, p2]):
        roots = [p]
        while len(roots) > 0:
            nroots = []
            for r in roots:
2170
                cw = pos[r][1] - w[r] / (2. * w[p])
2171
                for v in sorted(r.out_neighbors(), key=lambda a: order[a]):
2172
2173
                    pos[v] = (0, 0)
                    if i == 0:
2174
                        pos[v][0] = pos[r][0] - 1 / L * .5 * aspect
2175
                    else:
2176
                        pos[v][0] = pos[r][0] + 1 / L * .5 * aspect
2177
                    pos[v][1] = cw + w[v] / (2. * w[p])
2178
2179
2180
2181
2182
2183
                    cw += w[v] / w[p]
                    nroots.append(v)
            roots = nroots
    return pos


2184
2185
2186
2187
2188
# Handle cairo contexts from cairocffi

try:
    import cairocffi
    import ctypes
2189
    pycairo_aux = ctypes.PyDLL(os.path.dirname(os.path.abspath(__file__)) + "/libgt_pycairo_aux.so")
2190
2191
    pycairo_aux.gt_PycairoContext_FromContext.restype = ctypes.c_void_p
    pycairo_aux.gt_PycairoContext_FromContext.argtypes = 3 * [ctypes.c_void_p]
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
    ctypes.pythonapi.PyList_Append.argtypes = 2 * [ctypes.c_void_p]
except ImportError:
    pass

def _UNSAFE_cairocffi_context_to_pycairo(cairocffi_context):
    # Sanity check. Continuing with another type would probably segfault.
    if not isinstance(cairocffi_context, cairocffi.Context):
        raise TypeError('Expected a cairocffi.Context, got %r'
                        % cairocffi_context)

    # Create a reference for PycairoContext_FromContext to take ownership of.
    cairocffi.cairo.cairo_reference(cairocffi_context._pointer)
    # Casting the pointer to uintptr_t (the integer type as wide as a pointer)
    # gets the context’s integer address.
    # On CPython id(cairo.Context) gives the address to the Context type,
    # as expected by PycairoContext_FromContext.
2208
    address = pycairo_aux.gt_PycairoContext_FromContext(
2209
2210
2211
2212
2213
2214
2215
2216
2217
        int(cairocffi.ffi.cast('uintptr_t', cairocffi_context._pointer)),
        id(cairo.Context),
        None)
    assert address
    # This trick uses Python’s C API
    # to get a reference to a Python object from its address.
    temp_list = []
    assert ctypes.pythonapi.PyList_Append(id(temp_list), address) == 0
    return temp_list[0]
2218

2219
# Bottom imports to avoid circular dependency issues
2220
2221
from .. inference import get_hierarchy_tree, NestedBlockState, BlockState, \
    OverlapBlockState