コード例 #1
0
ファイル: layoutVdxRecord.py プロジェクト: ostwald/python-lib
class LayoutVdxRecord (VdxRecord):
	"""
	Extends VdxRecord to compute a graph layout (self.layout)
	
	layout args:
	- iterations - Number of FDL iterations to run in coordinate generation
    - force_strength - Strength of Coulomb and Hooke forces
                     (edit this to scale the distance between nodes)
    - dampening - Multiplier to reduce force applied to nodes
    - max_velocity - Maximum distance a node can move in one step
    - max_distance - The maximum distance considered for interactions
	
	"""
	verbose = 0
	origin = [5,5] # to place center near center of page
	layout_args = {
		'force_strength' : 2, #1.2, # determine spread - smaller number shorter edges
		'max_velocity' : 6.0, # doesn't seem to matter too much
		'iterations' : 5000,
		'max_distance' : 200 # default is 50 - 200 looks better, 2 is bad
	}
	
	def __init__(self, graphData):
		
		VdxRecord.__init__(self)
		self.graphData = graphData
		self.colors = Colors()
		# self.graphData = self.GraphDataImpl()
		"""
		up to the first 7 elements work, from then on the VDX cannot be imported!
		e.g., 
		# self.edgeData = self.graphData.getEdgeData()[:6] # works
		# self.edgeData = self.graphData.getEdgeData()[:7] # does NOT work
		"""
		# self.edgeData = self.graphData.getEdgeData()
		self.edgeData = self.graphData.getEdgeData()

		if len(self.edgeData) == 0:
			self.addEmptyGraphNode()
			return

		self.layout = Layout (self.edgeData, **self.layout_args)
		if self.verbose:
			print self.layout
	
		self.addNodes();
		
		self.addEdges();
		
	def _shapeGetter (self, shapeId):
		return self.getShapeByName(shapeId)
		
	def __repr__ (self):
		"""
		remove spaces so they won't be seen by Lucid,
		which for some reason does not ingore them
		"""
		return self.dom.toxml()
		
	def addEmptyGraphNode(self):
		main = {
			'name' : "main",
			'x' : 2,
			'y' : 8.5,
			'width' : 2.5,
			'height' : 2,
			'label': {
				'text': 'Empty Graph!',
				'color': '#0000ff',
				'size':32
			}
		}
		
		details = {
			'name' : "details",
			'x' : 1.5,
			'y' : 7.0,
			'width' : 3.5,
			'height' : .5,
			'label': {
				'text': 'There were no relations to show',
				'color': '#0000ff',
				'size':14
			}
		}			
		
		self.addNode(Circle, main)
		self.addNode(Rectangle, details)
		self.addEdge("main", "details", "details")
		
	def addNodes(self):
		"""
		use location from layout to create nodes (Shapes) and 
		insert them in this record
		"""
		
		def getName (id):
			return self.graphData.data_table.getNameFromId(id)
		
		# create the args that will define shapes in vdx
		for id, data in self.layout.nodes.iteritems():
			
			# print ' - %s: %s' % (id, getName(id))
			record = self.graphData.data_table.get(id)
			orgType = None
			if (record):
				orgType = record['OrganismType']

			if 0:
				print '-------'
				print ' - %s: %s (%s)' % (id, getName(id), orgType)
				print ' - text: %s' % self.colors.getColor(orgType,'text')
			
			pos = data['location']
			args = {
				'name': id, 
				'label': {
					'text':getName(id),
					'size':10,
					'color':self.colors.getColor(orgType,'text') or '#000000'
				},
				'line' : {
					'color':self.colors.getColor(orgType,'line') or '#000000'
				},
				# 'label': {'text':id},
				'x':pos[0] + self.origin[0],
				'y':pos[1] + self.origin[1], 	
				'width':1.0,
				'height':0.5,
				'color':'#2e62ff'
			}
			
			# print "adding shape at %s, %s" % (args['x'], args['y'])
			id = self.addNode(Circle, args)
			
			# sanity/debugging check
			shape = self.getShape(id)
			
			if not shape:
				# print vdx
				# print '-----------'
				# print 'shape keys:'
				# for key in vdx.shapes.keys():
					# print ' - %s (%s)' % (key, type(key))
				print "ERROR shape not found for added shape (%s) ... HALTING" % id
				sys.exit()
			if self.verbose:
				print ' ... (%s - %f, %f)' % (shape.Name, shape.PinX, shape.PinY)
			
	def makeEdgeShapeOFF (self, fromShape, toShape, relation, id):
		"""
		given two endpoints (the connectors) ...
		PinX is midpoint of x's
		PinY is midpoint of y's
		
		this function has to compute the input parameters to
		the Line constructor (to be implemented)
		
		XForm1D is inserted in Shape
		ObjectType = 2
		
		"""
		if self.verbose:
			print 'LayoutVDX - makeEdgeShape()'
		def avg (a, b):
			return (float(a) + float(b)) / 2.00000000
			
		def diff (a, b):
			return (float(a) - float(b))
		
		start = fromShape.getBestConnection(toShape)
		end = toShape.getBestConnection(fromShape)
		
		if self.verbose:
			print 'makeConnectorShape source=%s, target=%s' % (fromShape.ID, toShape.ID)
			print ' - start: %f, %f' % (start[0], start[1])
			print ' -   end: %f, %f' % (end[0], end[1])
		
		line_args = {
			'name': 'connector_%d' % (len(self.edges)+1),
			'label':{'text':relation, 'size':10},
			'x' : start[0],
			'y' : start[1],
			'height' : diff (end[1], start[1]) or util.pt2in(2),
			'width' : diff (end[0],start[0]),
			'begin_x' : start[0],
			'begin_y' : start[1],
			'end_x' : end[0],
			'end_y' : end[1]
		}
		
		# print 'line_args'
		# for k, v in line_args.iteritems():
			# print ' - %s: %s' % (k,v)
		
		return Line (id, line_args)

	def addEdges (self):
		"""
		create lines (Shapes) to represent the graph edges.
		insert the lines into the DOM
		
		NOTE: we are not making explicit connections at this time.
		they do not seem to be necessary, since the shapes when imported
		into lucid have connections anyway
		"""
		for data in self.layout.edges:

			source = data['source']
			target = data['target']
			
			if self.verbose:
				print 'source: %s target: %s'  % (source, target)
			self.addEdge (source, target, data['relation'])			
			

	def showEdgeData (self):
		print 'EdgeData (%d)' % len(self.edgeData)
		for item in self.edgeData:
			if 1:
				print item
			else:
				print ' - source: %s, target: %s, relation: %s' % \
					(item['source'], item['target'], item['relation'])