def __init__(self, total_size, duration, args): self._total_size = total_size self._duration = duration self._args = args self._tracker = AncestryTracker() self._num_pieces = 0 self._unit = args.unit if args.growth_time_limit: self._tracker.growth_time_limit = args.growth_time_limit if args.output_type != "dot": if args.edge_style == LINE: if args.stroke_style == SHRINKING: self._edge_plot_method = self.draw_shrinking_line else: self._edge_plot_method = self.draw_line elif args.edge_style in [CURVE, SPLINE]: if args.stroke_style == SHRINKING: self._edge_plot_method = self.draw_shrinking_curve else: self._edge_plot_method = self.draw_curve if args.stroke_style == SHRINKING: self._path_plot_method = self._draw_shrinking_path else: self._path_plot_method = self.draw_path if args.geometry == RECT: self._position = self._rect_position elif args.geometry == CIRCLE: self._position = self._circle_position
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)
class AncestryPlotter: def __init__(self, total_size, duration, args): self._total_size = total_size self._duration = duration self._args = args self._tracker = AncestryTracker() self._num_pieces = 0 self._unit = args.unit if args.growth_time_limit: self._tracker.growth_time_limit = args.growth_time_limit if args.output_type != "dot": if args.edge_style == LINE: if args.stroke_style == SHRINKING: self._edge_plot_method = self.draw_shrinking_line else: self._edge_plot_method = self.draw_line elif args.edge_style in [CURVE, SPLINE]: if args.stroke_style == SHRINKING: self._edge_plot_method = self.draw_shrinking_curve else: self._edge_plot_method = self.draw_curve if args.stroke_style == SHRINKING: self._path_plot_method = self._draw_shrinking_path else: self._path_plot_method = self.draw_path if args.geometry == RECT: self._position = self._rect_position elif args.geometry == CIRCLE: self._position = self._circle_position def set_size(self, width, height): self._width = width self._height = height if self._args.canvas_width: self._canvas_width = self._args.canvas_width else: self._canvas_width = width if self._args.canvas_height: self._canvas_height = self._args.canvas_height else: self._canvas_height = height @staticmethod def add_parser_arguments(parser): try: parser.add_argument("-width", type=int, default=2000) parser.add_argument("-height", type=int, default=2000) except argparse.ArgumentError: pass parser.add_argument("--canvas-width", type=float) parser.add_argument("--canvas-height", type=float) parser.add_argument("--edge-style", choices=[LINE, CURVE, SPLINE], default=LINE) parser.add_argument("--node-size", type=float, default=3) parser.add_argument("--root-node-size", type=float, default=9) parser.add_argument("--geometry", choices=GEOMETRIES, default=GEOMETRIES[0]) parser.add_argument("--stroke-style", choices=[PLAIN, SHRINKING]) parser.add_argument("-stroke-width", type=float, default=2) parser.add_argument("-stroke-color", type=str, default="black") parser.add_argument("--output-type", choices=OUTPUT_TYPES.keys(), default="svg") parser.add_argument("--unit", type=str, default="") parser.add_argument("--growth-time-limit", type=float) def add_piece(self, piece_id, t, begin, end): self._tracker.add(Piece(piece_id, t, begin, end)) self._num_pieces += 1 def _rect_position(self, t, byte_pos): y = (1 - t / self._duration) * self._width x = float(byte_pos) / self._total_size * self._height return Vector2d(x, y) def _circle_position(self, t, byte_pos): angle = float(byte_pos) / self._total_size * 2*math.pi x = self._width / 2 + (1 - t / self._duration) * self._width / 2 * math.cos(angle) y = self._height / 2 + (1 - t / self._duration) * self._height / 2 * math.sin(angle) return Vector2d(x, y) def plot(self): self._override_recursion_limit() for piece in self._tracker.last_pieces(): self._follow_piece(piece) def _override_recursion_limit(self): sys.setrecursionlimit(max(self._num_pieces, sys.getrecursionlimit())) def _follow_piece(self, piece, child=None): self.plot_piece(piece, child) if len(piece.growth) > 0: path = [(piece.t, (piece.begin + piece.end) / 2)] for older_version in reversed(piece.growth): path.append((older_version.t, (older_version.begin + older_version.end) / 2)) self._path_plot_method(path) for parent in piece.parents.values(): self._connect_generations(parent, piece, child) self._follow_piece(parent, piece) def plot_piece(self, piece, child): pass def _connect_generations(self, parent, child, grandchild, parent_t=None): if parent_t is None: parent_t = parent.t parent_pos = self._position(parent_t, (parent.begin + parent.end) / 2) child_pos = self._position(child.t, (child.begin + child.end) / 2) self._stroke_width1 = self._stroke_width_at_time(child.t) self._stroke_width2 = self._stroke_width_at_time(parent_t) if self._args.edge_style == SPLINE and grandchild is not None: grandchild_pos = self._position(grandchild.t, (grandchild.begin + grandchild.end) / 2) control_point = self._spline_control_point(parent_pos, child_pos, grandchild_pos) self._draw_spline(parent_pos, child_pos, control_point) else: self._edge_plot_method(child_pos.x, child_pos.y, parent_pos.x, parent_pos.y) def _spline_control_point(self, parent, child, grandchild): return grandchild + (child - grandchild)*1.3 def _draw_spline(self, p1, p2, p_control): self._write_svg('<path style="stroke:%s;stroke-opacity=0.5;fill:none;stroke-width:%f" d="M%f,%f Q%f,%f %f,%f" />' % ( self._args.stroke_color, self._args.stroke_width, p1.x, p1.y, p_control.x, p_control.y, p2.x, p2.y)) def _stroke_width_at_time(self, t): return self._args.stroke_width * (1 - pow(t/self._duration, 0.6)) def is_root_piece(self, piece): return (piece.end - piece.begin) == self._total_size
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