Files
rdkit/Python/DataStructs/HierarchyVis.py

122 lines
3.6 KiB
Python
Executable File

# $Id$
#
# Copyright (C)2003-2006 Rational Discovery LLC
#
# @@ All Rights Reserved @@
#
""" functionality for drawing hierarchical catalogs on sping
canvases
"""
from sping import pid as piddle
class VisOpts(object):
circRad = 10
minCircRad = 4
maxCircRad = 16
circColor = piddle.Color(0.6,0.6,0.9)
terminalEmptyColor = piddle.Color(.8,.8,.2)
terminalOnColor = piddle.Color(0.8,0.8,0.8)
terminalOffColor = piddle.Color(0.2,0.2,0.2)
outlineColor = piddle.transparent
lineColor = piddle.Color(0,0,0)
lineWidth = 1
horizOffset = 5
vertOffset = 75
topMargin = 20
labelFont = piddle.Font(face='helvetica',size=10)
highlightColor = piddle.Color(1.,1.,.4)
highlightWidth = 2
visOpts = VisOpts()
def GetMinCanvasSize(adjList,levelList):
maxAcross = -1
for k in levelList.keys():
nHere = len(levelList[k])
maxAcross = max(maxAcross,nHere)
nLevs = len(levelList.keys())
minSize = ( maxAcross*(visOpts.minCircRad*2+visOpts.horizOffset),
visOpts.topMargin + nLevs * visOpts.vertOffset )
return minSize
def DrawHierarchy(adjList,levelList,canvas,entryColors=None,
bitIds=None,minLevel=-1,maxLevel=1e8):
"""
Arguments:
- adjList: adjacency list representation of the hierarchy to be drawn
- levelList: dictionary mapping level -> list of ids
"""
if bitIds is None:
bitIds = []
if entryColors is None:
entryColors = {}
levelLengths = levelList.keys()
levelLengths.sort()
minLevel = max(minLevel,levelLengths[0])
maxLevel = min(maxLevel,levelLengths[-1])
dims = canvas.size
drawLocs = {}
# start at the bottom of the hierarchy and work up:
for levelLen in range(maxLevel,minLevel-1,-1):
nLevelsDown = levelLen-minLevel
pos = [0,visOpts.vertOffset*nLevelsDown+visOpts.topMargin]
ids = levelList.get(levelLen,[])
# FIX: we'll eventually want to figure out some kind of sorting here:
nHere = len(ids)
canvas.defaultFont=visOpts.labelFont
if nHere:
# figure the size of each node at this level:
spacePerNode = float(dims[0]) / nHere
spacePerNode -= visOpts.horizOffset
nodeRad = max(spacePerNode/2,visOpts.minCircRad)
nodeRad = min(nodeRad,visOpts.maxCircRad)
spacePerNode = nodeRad*2+visOpts.horizOffset
# start in the midde of the canvas:
pos[0] = dims[0]/2.
# maybe we need to offset a little:
if nHere%2:
pos[0] -= spacePerNode/2
# move to the left by half the number of nodes:
pos[0] -= (nHere // 2 - .5) * spacePerNode
# Find the locations and draw connectors:
for id in ids:
if not bitIds or id in bitIds:
# first do lines down to the next level:
if levelLen != maxLevel:
for neighbor in adjList[id]:
if drawLocs.has_key(neighbor):
p2 = drawLocs[neighbor][0]
canvas.drawLine(pos[0],pos[1],p2[0],p2[1],
visOpts.lineColor,visOpts.lineWidth)
drawLocs[id] = tuple(pos),nodeRad
pos[0] += spacePerNode
for id in drawLocs.keys():
pos,nodeRad = drawLocs[id]
x1,y1 = pos[0]-nodeRad,pos[1]-nodeRad
x2,y2 = pos[0]+nodeRad,pos[1]+nodeRad
drawColor = entryColors.get(id,visOpts.circColor)
canvas.drawEllipse(x1,y1,x2,y2,visOpts.outlineColor,
0,drawColor)
label = str(id)
#txtLoc = ( pos[0]-canvas.stringWidth(label)/2,
# pos[1]+canvas.fontHeight()/4 )
txtLoc = ( pos[0]+canvas.fontHeight()/4,
pos[1]+canvas.stringWidth(label)/2)
canvas.drawString(label,txtLoc[0],txtLoc[1],angle=90)
return drawLocs