class Ancestry(visualizer.Visualizer): def __init__(self, pieces, args): visualizer.Visualizer.__init__(self, args) self._tracker = AncestryTracker() for chunk in pieces: self._tracker.add(Piece(chunk["id"], chunk["t"], chunk["begin"], chunk["end"])) self._nodes = {} self._override_recursion_limit() for piece in self._tracker.last_pieces(): self._follow_piece(piece) def _follow_piece(self, piece): node = Node(piece.id) for parent in piece.parents.values(): node.add_edge(parent.id) self._follow_piece(parent) self._nodes[piece.id] = node def _override_recursion_limit(self): sys.setrecursionlimit(max(len(self._tracker.pieces()), sys.getrecursionlimit())) def render(self): self._update() glColor3f(0,0,0) self._min_x = min([node.position.x for node in self._nodes.values()]) self._max_x = max([node.position.x for node in self._nodes.values()]) self._min_y = min([node.position.y for node in self._nodes.values()]) self._max_y = max([node.position.y for node in self._nodes.values()]) for node in self._nodes.values(): self._render_node(node) def _render_node(self, node): glPointSize(3.0) glBegin(GL_POINTS) px = self._px(node.position.x) py = self._py(node.position.y) glVertex2f(px, py) glEnd() glBegin(GL_LINES) for other_node_id in node.edges: other_node = self._nodes[other_node_id] glVertex2f(px, py) glVertex2f(self._px(other_node.position.x), self._py(other_node.position.y)) glEnd() def _px(self, x): return (x - self._min_x) / (self._max_x - self._min_x) * self.width def _py(self, y): return (y - self._min_y) / (self._max_y - self._min_y) * self.height def _update(self): for node in self._nodes.values(): node.force = Vector2d(0,0) for node in self._nodes.values(): self._get_node_force(node) for node in self._nodes.values(): self._apply_node_force(node) def _get_node_force(self, node): for other_node_id in node.edges: other_node = self._nodes[other_node_id] self.apply_hooke_attraction(node, other_node) self.apply_hooke_attraction(other_node, node) for other_node in self._nodes.values(): if other_node != node: self.apply_coulomb_repulsion(node, other_node) def _apply_node_force(self, node): node.velocity += node.force * 0.01 node.position += node.velocity def apply_coulomb_repulsion(self, f, other): d = f.position - other.position distance = d.mag() if distance == 0: f.force += Vector2d(random.uniform(0.0, 0.0001), random.uniform(0.0, 0.0001)) else: d.normalize() f.force += d / pow(distance, 2) def apply_hooke_attraction(self, f, other): d = other.position - f.position f.force += d