# $Id$ # # Copyright (C)2003-2006 Rational Discovery LLC # # @@ All Rights Reserved @@ # This file is part of the RDKit. # The contents are covered by the terms of the BSD license # which is included in the file license.txt, found at the root # of the RDKit source tree. # """ 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