Browse code

Jump between nodes and edges.

Jose.R.Fonseca authored on 03/01/2008 12:40:00
Showing 1 changed files

  • xdot.py index a387bcc..71c4afc 100644
... ...
@@ -56,10 +56,6 @@ class Shape:
56 56
 		"""Draw this shape with the given cairo context"""
57 57
 		raise NotImplementedError
58 58
 
59
-	def boundingbox(self):
60
-		"""Get the bounding box of this shape."""
61
-		raise NotImplementedError
62
-
63 59
 
64 60
 class TextShape(Shape):
65 61
 	
... ...
@@ -222,31 +218,63 @@ class Element(CompoundShape):
222 218
 	def get_url(self, x, y):
223 219
 		return None
224 220
 
221
+	def get_jump(self, x, y):
222
+		return None
223
+
225 224
 
226 225
 class Node(Element):
227 226
 
228 227
 	def __init__(self, x, y, w, h, shapes, url):
229 228
 		Element.__init__(self, shapes)
230 229
 		
231
-		self.x1 = x - w/2
232
-		self.y1 = y - h/2
233
-		self.x2 = x + w/2
234
-		self.y2 = y + h/2
230
+		self.x = x
231
+		self.y = y
232
+
233
+		self.x1 = x - 0.5*w
234
+		self.y1 = y - 0.5*h
235
+		self.x2 = x + 0.5*w
236
+		self.y2 = y + 0.5*h
235 237
 		
236 238
 		self.url = url
237 239
 
240
+	def is_inside(self, x, y):
241
+		return self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2
242
+
238 243
 	def get_url(self, x, y):
239 244
 		if self.url is None:
240 245
 			return None
241 246
 		#print (x, y), (self.x1, self.y1), "-", (self.x2, self.y2)
242
-		if self.x1 <= x and x <= self.x2 and self.y1 <= y and y <= self.y2:
247
+		if self.is_inside(x, y):
243 248
 			return self.url
244 249
 		return None
245 250
 
251
+	def get_jump(self, x, y):
252
+		if self.is_inside(x, y):
253
+			return self.x, self.y
254
+		return None
255
+
256
+
257
+def square_distance(x1, y1, x2, y2):
258
+	deltax = x2 - x1
259
+	deltay = y2 - y1
260
+	return deltax*deltax + deltay*deltay
261
+
246 262
 
247 263
 class Edge(Element):
248 264
 
249
-	pass
265
+	def __init__(self, points, shapes):
266
+		Element.__init__(self, shapes)
267
+
268
+		self.points = points
269
+
270
+	RADIUS = 10
271
+
272
+	def get_jump(self, x, y):
273
+		if square_distance(x, y, *self.points[0]) <= self.RADIUS*self.RADIUS:
274
+			return self.points[-1]
275
+		if square_distance(x, y, *self.points[-1]) <= self.RADIUS*self.RADIUS:
276
+			return self.points[0]
277
+		return None
250 278
 
251 279
 
252 280
 class Graph(Shape):
... ...
@@ -280,6 +308,17 @@ class Graph(Shape):
280 308
 				return url
281 309
 		return None
282 310
 
311
+	def get_jump(self, x, y):
312
+		for edge in self.edges:
313
+			jump = edge.get_jump(x, y)
314
+			if jump is not None:
315
+				return jump
316
+		for node in self.nodes:
317
+			jump = node.get_jump(x, y)
318
+			if jump is not None:
319
+				return jump
320
+		return None
321
+
283 322
 
284 323
 class XDotAttrParser:
285 324
 	"""Parser for xdot drawing attributes.
... ...
@@ -441,7 +480,7 @@ class XDotParser:
441 480
 		for node in graph.get_node_list():
442 481
 			if node.pos is None:
443 482
 				continue
444
-			x, y = map(float, node.pos.split(","))
483
+			x, y = self.parse_node_pos(node.pos)
445 484
 			w = float(node.width)*72
446 485
 			h = float(node.height)*72
447 486
 			shapes = []
... ...
@@ -454,16 +493,36 @@ class XDotParser:
454 493
 				nodes.append(Node(x, y, w, h, shapes, url))
455 494
 
456 495
 		for edge in graph.get_edge_list():
496
+			if edge.pos is None:
497
+				continue
498
+			points = self.parse_edge_pos(edge.pos)	
457 499
 			shapes = []
458 500
 			for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
459 501
 				if hasattr(edge, attr):
460 502
 					parser = XDotAttrParser(self, getattr(edge, attr))
461 503
 					shapes.extend(parser.parse())
462 504
 			if shapes:
463
-				edges.append(Edge(shapes))
505
+				edges.append(Edge(points, shapes))
464 506
 
465 507
 		return Graph(width, height, nodes, edges)
466 508
 
509
+	def parse_node_pos(self, pos):
510
+		x, y = pos.split(",")
511
+		return self.transform(float(x), float(y))
512
+
513
+	def parse_edge_pos(self, pos):
514
+		points = []
515
+		for entry in pos.split(' '):
516
+			fields = entry.split(',')
517
+			try:
518
+				x, y = fields
519
+			except ValueError:
520
+				# TODO: handle start/end points
521
+				continue
522
+			else:
523
+				points.append(self.transform(float(x), float(y)))
524
+		return points
525
+
467 526
 	def transform(self, x, y):
468 527
 		# XXX: this is not the right place for this code
469 528
 		x = (x + self.xoffset)*self.xscale
... ...
@@ -472,6 +531,7 @@ class XDotParser:
472 531
 
473 532
 
474 533
 class DotWidget(gtk.DrawingArea):
534
+	"""PyGTK widget that draws dot graphs."""
475 535
 
476 536
 	__gsignals__ = {
477 537
 		'expose-event': 'override',
... ...
@@ -613,6 +673,13 @@ class DotWidget(gtk.DrawingArea):
613 673
 			self.emit('clicked', unicode(url), event)
614 674
 			return True
615 675
 
676
+		jump = self.get_jump(x, y)
677
+		x, y = self.window2graph(x, y)
678
+		if jump is not None:
679
+			jumpx, jumpy = jump
680
+			self.x += jumpx - x
681
+			self.y += jumpy - y
682
+			self.queue_draw()
616 683
 		return False
617 684
 
618 685
 	def on_area_button_release(self, area, event):
... ...
@@ -652,7 +719,7 @@ class DotWidget(gtk.DrawingArea):
652 719
 
653 720
 		return True
654 721
 
655
-	def get_url(self, x, y):
722
+	def window2graph(self, x, y):
656 723
 		rect = self.get_allocation()
657 724
 		x -= 0.5*rect.width
658 725
 		y -= 0.5*rect.height
... ...
@@ -660,9 +727,16 @@ class DotWidget(gtk.DrawingArea):
660 727
 		y /= self.zoom_ratio
661 728
 		x += self.x
662 729
 		y += self.y
663
-		y = self.graph.height - y
730
+		return x, y
731
+
732
+	def get_url(self, x, y):
733
+		x, y = self.window2graph(x, y)
664 734
 		return self.graph.get_url(x, y)
665 735
 
736
+	def get_jump(self, x, y):
737
+		x, y = self.window2graph(x, y)
738
+		return self.graph.get_jump(x, y)
739
+
666 740
 
667 741
 class DotWindow(gtk.Window):
668 742