class Waves(visualizer.Visualizer): def __init__(self, args): self._gathered_segments_layer = None visualizer.Visualizer.__init__(self, args, file_class=File, peer_class=Peer, segment_class=Segment) if self.args.peer_info: self.layout_managers = {"left": LayoutManager1d(), "right": LayoutManager1d()} self.waves_margin = self.parse_margin_argument(self.args.waves_margin) @staticmethod def add_parser_arguments(parser): visualizer.Visualizer.add_parser_arguments(parser) parser.add_argument("--peer-info", action="store_true") parser.add_argument("--enable-title", action="store_true") parser.add_argument("--title-size", type=float, default=30.0) parser.add_argument("--test-title", type=str) visualizer.Visualizer.add_margin_argument(parser, "--waves-margin") def resized_window(self): self.waves_margin.update() self._waves_width = self.width - self.waves_margin.left - self.waves_margin.right self._waves_right = self.width - self.waves_margin.right self._waves_height = self.height - self.waves_margin.top - self.waves_margin.bottom self._waves_top = self.height - self.waves_margin.top self._title_renderer = None def configure_2d_projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0.0, self.window_width, 0.0, self.window_height, -1.0, 1.0) glMatrixMode(GL_MODELVIEW) def synth_address_received(self): self.subscribe_to_waveform() def reset(self): visualizer.Visualizer.reset(self) self.playing_segments = collections.OrderedDict() self.gatherer = Gatherer() if self._gathered_segments_layer: self._gathered_segments_layer.refresh() self._first_segment_received = False self._title_renderer = None def pan_segment(self, segment): # let orchestra & synth spatialize pass def InitGL(self): visualizer.Visualizer.InitGL(self) glClearColor(0.0, 0.0, 0.0, 0.0) self._gathered_segments_layer = self.new_layer(self._render_gathered_segments) def update(self): outdated = [] for segment in self.playing_segments.values(): if not segment.is_playing(): self.gatherer.add(segment) outdated.append(segment.id) if len(outdated) > 0: for segment_id in outdated: self.playing_segments[segment_id].free() del self.playing_segments[segment_id] self._gathered_segments_layer.refresh() def render(self): self._set_gathered_color() self._gathered_segments_layer.draw() self.draw_playing_segments() if self.args.enable_title: if not self._title_renderer and (self._first_segment_received or self.args.test_title): self._create_title_renderer() if self._title_renderer: self._render_title() def _create_title_renderer(self): if self.args.test_title: title = self.args.test_title else: title = self.torrent_title size = self.args.title_size / 1024 * self.width self._title_renderer = TitleRenderer(title, size, self) def _render_title(self): if self.download_completed(): color = self._fade_out_color() else: color = Vector3d(1,1,1) glColor3f(*color) x = self.waves_margin.left + 10.0 / 640 * self.width y = self.height * 0.03 self._title_renderer.render(x, y) def _set_gathered_color(self): if self.download_completed(): self.gathered_color = self._fade_out_color() self._gathered_segments_layer.refresh() elif self.torrent_length > 0: torrent_progress = float(self.gatherer.gathered_bytes()) / self.torrent_length self.gathered_color = GATHERED_COLOR + (WAVEFORM_COLOR - GATHERED_COLOR) * pow(torrent_progress, 20) else: self.gathered_color = GATHERED_COLOR def _fade_out_color(self): time_after_completion = max(self.now - self.torrent_download_completion_time, 0) if time_after_completion > FADE_OUT_DURATION: return Vector3d(0,0,0) else: return WAVEFORM_COLOR * pow(1 - time_after_completion/FADE_OUT_DURATION, 0.15) def active(self): return len(self.playing_segments) > 0 def finished(self): if self.download_completed(): time_after_completion = max(self.now - self.torrent_download_completion_time, 0) if time_after_completion > FADE_OUT_DURATION: return True def draw_playing_segments(self): glEnable(GL_LINE_SMOOTH) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) for segment in self.playing_segments.values(): segment.render() def _render_gathered_segments(self): glBegin(GL_QUADS) x1 = self.waves_margin.left x2 = self._waves_right min_height = GATHERED_LINE_WIDTH * self._waves_height for segment in self.gatherer.pieces(): y1 = self.byte_to_py(segment.torrent_begin) y2 = max(self.byte_to_py(segment.torrent_end), y1 + min_height) if (y2 - y1) > min_height: d = min((y2 - y1) * 0.2, MAX_GRADIENT_HEIGHT * self._waves_height) y1d = y1 + d y2d = y2 - d glColor3f(0, 0, 0) glVertex2f(x1, y1) glColor3f(*self.gathered_color) glVertex2f(x1, y1d) glVertex2f(x2, y1d) glColor3f(0, 0, 0) glVertex2f(x2, y1) glColor3f(0, 0, 0) glVertex2f(x1, y2) glColor3f(*self.gathered_color) glVertex2f(x1, y2d) glVertex2f(x2, y2d) glColor3f(0, 0, 0) glVertex2f(x2, y2) glColor3f(*self.gathered_color) glVertex2f(x1, y1d) glVertex2f(x1, y2d) glVertex2f(x2, y2d) glVertex2f(x2, y1d) else: glColor3f(0, 0, 0) glVertex2f(x1, y1) glColor3f(*self.gathered_color) glVertex2f(x1, y2) glVertex2f(x2, y2) glColor3f(0, 0, 0) glVertex2f(x2, y1) glEnd() def byte_to_py(self, byte): return int(self.waves_margin.bottom + self.byte_to_relative_y(byte) * self._waves_height) def byte_to_relative_y(self, byte): return float(byte) / self.torrent_length def handle_segment_waveform_value(self, segment, value): segment.append_to_waveform(value)
class HeatMap(visualizer.Visualizer): def __init__(self, args): self._history_layer = None self._load_locations() visualizer.Visualizer.__init__( self, args, file_class=File, peer_class=Peer, segment_class=Segment) self._set_horizontal_scope() self._set_verical_scope() self._map_margin = self.parse_margin_argument(self.args.map_margin) def _set_horizontal_scope(self): self._hscope_min, self._hscope_max = map(float, self.args.hscope.split(":")) self._hscope_size = self._hscope_max - self._hscope_min def _set_verical_scope(self): self._vscope_min, self._vscope_max = map(float, self.args.vscope.split(":")) self._vscope_size = self._vscope_max - self._vscope_min @staticmethod def add_parser_arguments(parser): visualizer.Visualizer.add_parser_arguments(parser) parser.add_argument("--title-size", type=float, default=30.0) parser.add_argument("--disable-title", action="store_true") parser.add_argument("--test-title", type=str) parser.add_argument("--hscope", type=str, default="0:1") parser.add_argument("--vscope", type=str, default="0:1") parser.add_argument("--continents", action="store_true") visualizer.Visualizer.add_margin_argument(parser, "--map-margin") def reset(self): visualizer.Visualizer.reset(self) self.playing_segments = collections.OrderedDict() self._first_segment_received = False self._title_renderer = None def InitGL(self): visualizer.Visualizer.InitGL(self) glClearColor(0.0, 0.0, 0.0, 0.0) self._history_layer = self.new_layer(self._render_history) if self.args.continents: import world self._world = world.World(1.0, 1.0) self._continents_layer = self.new_layer(self._render_continents) def resized_window(self): self._map_margin.update() self._map_width = self.width - self._map_margin.left - self._map_margin.right self._map_height = self.height - self._map_margin.top - self._map_margin.bottom self._size_factor = (float(self._map_width) + self._map_height) / 2 / ((1024 + 768) / 2) self._marker_lists = [] for n in range(0, MARKER_PRECISION): display_list = self.new_display_list_id() self._marker_lists.append(display_list) glNewList(display_list, GL_COMPILE) self._render_marker_circle(n) glEndList() def configure_2d_projection(self): glMatrixMode(GL_PROJECTION) glLoadIdentity() glOrtho(0.0, self.window_width, 0.0, self.window_height, -1.0, 1.0) glMatrixMode(GL_MODELVIEW) def _load_locations(self): self._locations = collections.defaultdict(int) for location in locations.get_locations(): self._add_location(location) self._max_frequency = max(self._locations.values()) def _add_location(self, location): if not location: return self._locations[Location(location)] += 1 if self._history_layer: self._history_layer.refresh() def added_segment(self, segment): self._first_segment_received = True self.playing_segments[segment.id] = segment def added_peer(self, peer): self._add_location(peer.location) def render(self): glEnable(GL_POINT_SMOOTH) glHint(GL_POINT_SMOOTH_HINT, GL_NICEST) glEnable(GL_LINE_SMOOTH) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self._update() if self.args.continents: self._continents_layer.draw() else: self._history_layer.draw() self._render_activity() if not self.args.disable_title: if not self._title_renderer and (self._first_segment_received or self.args.test_title): self._create_title_renderer() if self._title_renderer: self._render_title() def _render_continents(self): glColor4f(0.8,0.8,0.8,1) glLineWidth(1.0) for path in self._world.paths: self._draw_path(path) def _draw_path(self, path): glBegin(GL_LINE_STRIP) for x, y in path: self._location_vertex(x, y) glEnd() def _create_title_renderer(self): if self.args.test_title: title = self.args.test_title else: title = self.torrent_title size = self.args.title_size * self._size_factor self._title_renderer = TitleRenderer(title, size, self) def _render_title(self): glColor3f(1,1,1) x = self._map_margin.left + 10.0 / 640 * self.width y = self.height * 0.03 self._title_renderer.render(x, y) def _render_history(self): glColor4f(0.8,0.8,0.8,1) for location, frequency in self._locations.iteritems(): point_size = pow(float(frequency) / self._max_frequency, 0.15) * 4 * self._size_factor #point_size = max(point_size, 3) glPointSize(point_size) glBegin(GL_POINTS) self._location_vertex(location.x, location.y) glEnd() def _location_vertex(self, location_x, location_y): p = Vector2d( (location_x - self._hscope_min) / self._hscope_size, (location_y - self._vscope_min) / self._vscope_size) glVertex2f( self._map_margin.left + p.x * self._map_width, self._map_margin.bottom + (1-p.y) * self._map_height) def _render_activity(self): glColor4f(1,1,1,1) for segment in self.playing_segments.values(): location = self.peers_by_addr[segment.peer.addr].location x, y = location # #n = int(MARKER_PRECISION * (((self.now - segment.peer.arrival_time) * 0.8))) % MARKER_PRECISION # n = clamp(int(segment.relative_size() * MARKER_PRECISION), 0, MARKER_PRECISION-1) # glPushMatrix() # glTranslatef(x * self.width, y * self.height, 0) # #self._render_marker_circle(n) # glCallList(self._marker_lists[n]) # glPopMatrix() #size = (1.0 + 0.3 * math.sin((self.now - segment.peer.arrival_time) * 2.0)) * 8 / 640 #glPointSize(size * self.width) glPointSize(max(int(segment.relative_size() * 10.0 * self._size_factor), 1)) glBegin(GL_POINTS) self._location_vertex(x, y) glEnd() def _render_marker_circle(self, n): # t = float(n) / MARKER_PRECISION * 2*math.pi # radius = (1.0 + 0.15 * math.sin(t)) * 8 / 640 stroke_radius = float(n) / MARKER_PRECISION * 8 / 640 shadow_inner_radius = float(n) / MARKER_PRECISION * 4 / 640 shadow_outer_radius = float(n) / MARKER_PRECISION * 12 / 640 glColor4f(0,0,0,0.5) glBegin(GL_QUADS) for i in range(20): a1 = float(i) / 20 * 2*math.pi a2 = float(i+1) / 20 * 2*math.pi x1 = shadow_outer_radius * math.cos(a1) * self.min_dimension y1 = shadow_outer_radius * math.sin(a1) * self.min_dimension x2 = shadow_inner_radius * math.cos(a1) * self.min_dimension y2 = shadow_inner_radius * math.sin(a1) * self.min_dimension x3 = shadow_inner_radius * math.cos(a2) * self.min_dimension y3 = shadow_inner_radius * math.sin(a2) * self.min_dimension x4 = shadow_outer_radius * math.cos(a2) * self.min_dimension y4 = shadow_outer_radius * math.sin(a2) * self.min_dimension glVertex2f(x1, y1) glVertex2f(x2, y2) glVertex2f(x3, y3) glVertex2f(x4, y4) glEnd() glColor4f(1,1,1,1) glLineWidth(2.0 * self._size_factor) glBegin(GL_LINE_LOOP) for i in range(20): a = float(i) / 20 * 2*math.pi cx = stroke_radius * math.cos(a) * self.min_dimension cy = stroke_radius * math.sin(a) * self.min_dimension glVertex2f(cx, cy) glEnd() def _update(self): self._delete_outdated_segments() def _delete_outdated_segments(self): outdated = [] for segment in self.playing_segments.values(): if not segment.is_playing(): outdated.append(segment.id) for segment_id in outdated: del self.playing_segments[segment_id]