diff options
author | Nostalghia <b.vehikel@googlemail.com> | 2010-06-27 16:57:23 (GMT) |
---|---|---|
committer | Nostalghia <b.vehikel@googlemail.com> | 2010-06-27 16:57:23 (GMT) |
commit | 5dce05c339d2fc636b845d7c01d2103c565447f6 (patch) | |
tree | b467ca648fe2feb876705e47ecb86c31b3b2c59b /ka_controller.py | |
parent | 22d182db24861239a33806c4869b2d6cc7228d8b (diff) |
refactoring th code for page handling
Diffstat (limited to 'ka_controller.py')
-rw-r--r-- | ka_controller.py | 597 |
1 files changed, 63 insertions, 534 deletions
diff --git a/ka_controller.py b/ka_controller.py index c5e2d3e..6fe2938 100644 --- a/ka_controller.py +++ b/ka_controller.py @@ -15,563 +15,91 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import gtk -import gobject -import cairo -import model_population -import model_random -import ka_debug -import ka_status -import ka_incoming -import ka_task import ka_extensionpoint -import kandidtube -import ka_history -import no_glade - -POPULATION_CAPACITY = 12 -INCOMMING_CAPACITY = 3 +import ka_debug class KandidController(object): """ - inv: self.model is not None - inv: self._widget_tree is not None + inv: self._widget_list is not None + inv: self._activity_root is not None """ - def __init__(self, init_widget_tree, init_activity_root): + def __init__(self, widget, activity_root): """ - pre: init_widget_tree is not None + pre: widget is not None """ - self._start_from_scratch = True - self._widget_tree = init_widget_tree - self.activity_root = init_activity_root - self._tube = None - self.surface_cache = {} - self._status = ka_status.Status.instance() - self._gencount = 0 - self._task_lock = -1 + self._widget = widget + self._widget_list = widget.widget_list + self._activity_root = activity_root - # create data model - self.model = model_population.KandidModel(POPULATION_CAPACITY) - self.model.randomize() - self.incoming = ka_incoming.KandidIncoming(INCOMMING_CAPACITY, - self._widget_tree) self._pages = [] - self._zoom_controller = None - self._details_controller = None - self._ancestors_controller = None - # add optional pages to kandid notebook - pages = ka_extensionpoint.list_extensions('page') - for page in pages: - page_controller = ka_extensionpoint.create(page, self, - self._widget_tree) - self._pages.append(page_controller) - page_controller.autoconnect_events() - page_controller.localize() - page_controller.show() - if str(type(page_controller)) == "<class 'ep_page_zoom.ZoomController'>": - self._zoom_controller = page_controller - if str(type(page_controller)) == "<class 'ep_page_details.DetailsController'>": - self._details_controller = page_controller - if str(type(page_controller)) == "<class 'ep_page_ancestors.AncestorsController'>": - self._ancestors_controller = page_controller - - self._update_population_gui() - - def autoconnect_events(self): - self._widget_tree.get_widget('breedGenerationButton') \ - .connect('clicked', self.on_breed_generation) - self._widget_tree.get_widget('randomGenerationButton') \ - .connect('clicked', self.on_random_generation) - self._widget_tree.get_widget('flurrySpinButton') \ - .connect('value-changed', self.on_flurry_value_changed) - for cell_index in xrange(self.model.size): - six = str(cell_index) - self._widget_tree.get_widget('drawingarea_' + six) \ - .connect('expose-event', self.on_drawingarea_expose) - self._widget_tree.get_widget('drawingarea_' + six) \ - .connect('size-allocate', self.on_drawingarea_size_allocate) - self._widget_tree.get_widget('fitness_' + six) \ - .connect('value-changed', self.on_fitness_value_changed) - menu = self._widget_tree.get_widget('protozoon_menu_' + six) - self._widget_tree.get_widget('open_popup_' + six) \ - .connect_object('button-press-event', self.on_protozoon_popup, menu) - self._widget_tree.get_widget('favorite_menuitem_' + six) \ - .connect('activate', self.on_favorite_activate) - self._widget_tree.get_widget('awfull_menuitem_' + six) \ - .connect('activate', self.on_awfull_activate) - self._widget_tree.get_widget('zoomprotozoon_menuitem_' + six) \ - .connect('activate', self.on_zoomprotozoon_activate) - self._widget_tree.get_widget('publishprotozoon_menuitem_' + six) \ - .connect('activate', self.on_publishprotozoon_activate) - self._widget_tree.get_widget('exportpng_menuitem_' + six) \ - .connect('activate', self.on_exportpng_activate) - self._widget_tree.get_widget('explain_menuitem_' + six) \ - .connect('activate', self.on_explain_activate) - self._widget_tree.get_widget('ancestors_menuitem_' + six) \ - .connect('activate', self.on_ancestors_activate) - - for cell_index in xrange(3): - six = str(cell_index) - menu = self._widget_tree.get_widget('incoming_menu_' + six) - self._widget_tree.get_widget('incomingbutton_' + six) \ - .connect_object('button-press-event', self.on_incoming_popup, menu) - self._widget_tree.get_widget('zoom_menuitem_' + six) \ - .connect('activate', self.on_zoom_incoming) - self._widget_tree.get_widget('accept_menuitem_' + six) \ - .connect('activate', self.on_accept_incoming) - self._widget_tree.get_widget('decline_menuitem_' + six) \ - .connect('activate', self.on_decline_incoming) -# self._widget_tree.get_widget('send_menuitem_' + str(cell_index)) \ -# .connect('activate', self.on_send_activate) -# self._widget_tree.get_widget('explain_menuitem_' + str(cell_index)) \ -# .connect('activate', self.on_explain_activate) - -# #Create a dictionary to connect events -# events = { -# 'on_breed_generation' : self.on_breed_generation, -# 'on_random_generation' : self.on_random_generation, -# 'on_flurry_value_changed' : self.on_flurry_value_changed, -# 'on_zoom_incoming' : self.on_zoom_incoming, -# 'on_accept_incoming' : self.on_accept_incoming, -# 'on_decline_incoming' : self.on_decline_incoming, -# 'delete_event' : gtk.main_quit } -# for cell_index in range(self.model.size): -# strix = str(cell_index) -# key = 'on_drawingarea_#_expose'.replace('#', strix) -# events[key] = self.on_drawingarea_expose -# key = 'on_drawingarea_#_size_allocate'.replace('#', strix) -# events[key] = self.on_drawingarea_size_allocate -# key = 'on_fitness_#_value_changed'.replace('#', strix) -# events[key] = self.on_fitness_value_changed -# events['on_protozoon_popup_' + strix] = self.on_protozoon_popup -# events['on_publishprotozoon_activate_' + strix] = \ -# self.on_publishprotozoon_activate -# events['on_zoomprotozoon_activate_' + strix] = \ -# self.on_zoomprotozoon_activate -# events['on_exportpng_activate_' + strix] = \ -# self.on_exportpng_activate -# events['on_explain_activate_' + strix] = \ -# self.on_explain_activate -# events['on_ancestors_activate_' + strix] = \ -# self.on_ancestors_activate -# events['on_favorite_activate_' + strix] = self.on_favorite_activate -# events['on_awfull_activate_' + strix] = self.on_awfull_activate -# for cell_index in range(3): -# strix = str(cell_index) -# key = 'on_incoming_#_popup'.replace('#', strix) -# events[key] = self.on_incoming_popup -# self._widget_tree.signal_autoconnect(events) - self.incoming.autoconnect_events() - gobject.timeout_add(1000, self.on_timer) - - def _update_model(self, in_model): - if in_model: - self.model = in_model -# self.model._state = model_population.STATE_EVOLVED - self.surface_cache = {} - self._update_population_gui() - - def _update_population_gui(self): - # update fitness - for cell_index in range(self.model.size): - strix = str(cell_index) - key = 'fitness_#'.replace('#', strix) - self._widget_tree.get_widget(key). \ - set_value(self.model.fitness[cell_index]) - # update flurry - self._widget_tree.get_widget('flurrySpinButton'). \ - set_value(self.model.flurry_rate) - self._update_generate_buttons() - - def _update_generate_buttons(self): - # update buttons - is_sensitive = ka_task.GeneratorTask.is_completed() - if is_sensitive: - dummy, moderate, poor = self.model.classify() - is_sensitive = len(poor) > 0 and len(moderate) > 0 -# ka_debug.info('_update_generate_buttons %s' % is_sensitive) - self._widget_tree.get_widget('breedGenerationButton'). \ - set_sensitive(is_sensitive) - self._widget_tree.get_widget('randomGenerationButton'). \ - set_sensitive(is_sensitive) - - def _draw_from_cache(self, widget, cell_index): - if self.surface_cache.has_key(cell_index): -# ka_debug.info('_draw_from_cache: ' + widget.name + ' ' -# + str(cell_index)) - ctx = create_context(widget) - ctx.set_operator(cairo.OPERATOR_SOURCE) - ctx.set_source_surface(self.surface_cache[cell_index]) - ctx.paint() - - def on_drawingarea_expose(self, widget, event): - """ Repaint image of a single protozoon inside population area. - pre: widget is not None - """ - # draw precalculated protozoon stored in the surface cache. -# ka_debug.info('on_drawingarea_expose: ' + widget.name + ' ' -# + str(widget.allocation.width) -# + 'x' + str(widget.allocation.height)) - self._draw_from_cache(widget, name_to_index(widget.name)) - - def on_drawingarea_size_allocate(self, widget, event): - """ New size for drawing area available. - pre: widget is not None - """ -# ka_debug.info('on_drawingarea_size_allocate: ' + widget.name + ' ' -# + str(widget.allocation.width) -# + 'x' + str(widget.allocation.height)) - self.start_calculation([name_to_index(widget.name)]) - - def on_fitness_value_changed(self, *args): - """ - pre: len(args) >= 1 - """ -# ka_debug.info('on_fitness_value_changed %f [%s]' % -# (args[0].get_value(), args[0].get_name())) - self.model.fitness[name_to_index(args[0].get_name())] \ - = args[0].get_value() - self._update_population_gui() - - def on_breed_generation(self, *args): - if ka_task.GeneratorTask.is_completed(): -# ka_debug.info('on_breed_generation entry') - self._gencount += 1 - ka_task.GeneratorTask(self.task_breed_generation, - self.on_model_completed, - 'breed_'+str(self._gencount)).start() -# ka_debug.info('on_breed_generation exit') - else: - ka_debug.info('on_breed_generation ignored') - - def on_random_generation(self, *args): - if ka_task.GeneratorTask.is_completed(): -# ka_debug.info('on_random_generation entry') - self._gencount += 1 - ka_task.GeneratorTask(self.task_random_generation, - self.on_model_completed, - 'random_'+str(self._gencount)).start() -# ka_debug.info('on_random_generation exit') - else: - ka_debug.info('on_random_generation ignored') - - def on_model_completed(self, *args): - """ - pre: len(args) == 1 - """ -# ka_debug.info('on_model_completed entry') - for cell_index in args[0]: - self._widget_tree.get_widget('vbox_' + str(cell_index)). \ - set_sensitive(False) - self.start_calculation(args[0]) -# ka_debug.info('on_model_completed exit') - - def on_timer(self, *args): - if self._start_from_scratch: - self._start_from_scratch = False - compl = ka_task.GeneratorTask.is_completed() - if self._task_lock == -1: -# ka_debug.info('on_timer %s %u' % (compl, self._task_lock)) - self._update_generate_buttons() - self._task_lock = 0 if compl else 1 - elif self._task_lock == 1: -# ka_debug.info('on_timer %s %u' % (compl, self._task_lock)) - self._update_generate_buttons() - self._task_lock = 0 if compl else 1 - elif self._task_lock == 0: # and not compl: -# ka_debug.info('on_timer %s %u' % (compl, self._task_lock)) - self._update_generate_buttons() - self._task_lock = 1 - return True - - def start_all_calculations(self): - self._start_from_scratch = False -# ka_debug.info('start_all_calculations %u' % (self.model.size)) - ka_debug.print_call_stack() - self.start_calculation([i for i in range(self.model.size)]) - - def start_calculation(self, concerned): - """ - pre: len(concerned) > 0 - pre: forall(concerned, lambda x: 0 <= x <= self.model.size) - """ - for cell_index in concerned: - widget_name = 'drawingarea_' + str(cell_index) - widget = self._widget_tree.get_widget(widget_name) - task = ka_task.GeneratorTask(self.task_render, - self.on_image_completed, - widget_name) - task.start(self.model.protozoans[cell_index], cell_index, - widget.allocation.width, widget.allocation.height) -# ka_debug.info('start_calculation %ux%u for %s' % -# (widget.allocation.width, widget.allocation.height, widget.name)) - - def task_breed_generation(self, task, *args, **kwargs): - """ - pre: len(args) == 0 - """ -# ka_debug.info('task_breed_generation entry') - new_indices = self.model.breed_generation() -# ka_debug.info('task_breed_generation exit') - return new_indices - - def task_breed_single(self, task, *args, **kwargs): - """ - pre: len(args) == 1 - """ -# ka_debug.info('task_breed_single entry') - new_indices = self.model.breed_single(args[0]) -# ka_debug.info('task_breed_single exit') - return new_indices - def task_random_generation(self, task, *args, **kwargs): + @staticmethod + def numeric_compare(page1, page2): """ - pre: len(args) == 0 + pre: page1 is not None + pre: page2 is not None """ -# ka_debug.info('task_random_generation entry') - new_indices = self.model.random() -# ka_debug.info('task_random_generation exit') - return new_indices + return page1.position - page2.position - def task_render(self, task, *args, **kwargs): + def create_pages(self): """ - pre: len(args) == 4 + Add optional pages to kandid notebook """ - protozoon, cell_index, width, height = \ - args[0], args[1], args[2], args[3] -# ka_debug.info('task_render entry: ' + str(cell_index)) - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - ctx = cairo.Context(surface) - protozoon.render(task, ctx, width, height) - self.surface_cache[cell_index] = surface - history = ka_history.KandidHistory.instance() - history.link_surface(protozoon.get_unique_id(), surface) -# ka_debug.info('task_render exit: ' + str(cell_index)) - return cell_index - - def on_image_completed(self, *args): -# ka_debug.info('on_image_completed: ' + str(args[0])) - cell_index = args[0] - widget = self._widget_tree.get_widget('drawingarea_' - + str(cell_index)) - self._draw_from_cache(widget, cell_index) - self._widget_tree.get_widget('vbox_' + str(cell_index)). \ - set_sensitive(True) - self._widget_tree.get_widget('fitness_' + str(cell_index)). \ - set_value(self.model.fitness[cell_index]) - - def on_flurry_value_changed(self, *args): - """ - pre: len(args) >= 1 - pre: 0 <= args[0].get_value() <= 9 - """ -# ka_debug.info('on_flurry_value_changed [%s]' % args[0].get_value()) - model_random.set_flurry(args[0].get_value()) - - def on_protozoon_popup(self, widget, event): -# ka_debug.info('on_protozoon_popup: ' + widget.name) - self._show_popup(widget, event, \ - 'protozoon_menu_%u' % name_to_index(widget.name)) - - def on_publishprotozoon_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ - ka_debug.info('on_publishprotozoon_activate [%s]' % args[0].get_name()) - if self._tube: - proto = self.model.protozoans[name_to_index(args[0].get_name())] - self._tube.publish_protozoon(model_population.to_buffer(proto)) - - def on_zoomprotozoon_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ -# ka_debug.info('on_zoomprotozoon_activate [%s]' % args[0].get_name()) - if self._zoom_controller is not None: - self._zoom_controller.start_calculation( - self.model.protozoans[name_to_index(args[0].get_name())].copy()) - - def on_exportpng_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ - ka_debug.info('on_exportpng_activate [%s]' % args[0].get_name()) - protozoon = self.model.protozoans[name_to_index(args[0].get_name())] - exporter = ka_extensionpoint.create('exporter_png', - protozoon, - self.activity_root) - exporter.export(480, 480) - - def on_explain_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ - ka_debug.info('on_explain_activate [%s]' % args[0].get_name()) - protozoon = self.model.protozoans[name_to_index(args[0].get_name())] - if self._details_controller is not None: - self._details_controller.start_calculation(protozoon) - -# def on_explain_activate(self, *args): -# """Explain single protozoon to all other buddies. -# pre: len(args) >= 1 -# """ -# ka_debug.info('on_explain_activate [%s]' % args[0].get_name()) -# protozoon = self.model.protozoans[name_to_index(args[0].get_name())] - - def on_send_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ - ka_debug.info('on_send_activate [%s]' % args[0].get_name()) - protozoon = self.model.protozoans[name_to_index(args[0].get_name())] -# glade_replacement = no_glade.NoGlade() -# sendDialog = glade_replacement.get_dialog_send() -# sendDialog.show() - - def on_ancestors_activate(self, *args): - """Publish single protozoon to all other buddies. - pre: len(args) >= 1 - """ - ka_debug.info('on_ancestors_activate [%s]' % args[0].get_name()) - protozoon = self.model.protozoans[name_to_index(args[0].get_name())] - if self._ancestors_controller is not None: - self._ancestors_controller.start_calculation(protozoon) - - def on_favorite_activate(self, *args): - """Set best ranking for this protozoon. - pre: len(args) >= 1 - """ -# ka_debug.info('on_favorite_activate [%s]' % args[0].get_name()) - self.model.raise_fitness(name_to_index(args[0].get_name())) - self._update_population_gui() + page_names = ka_extensionpoint.list_extensions('page') + for name in page_names: + page_controller = ka_extensionpoint.create(name, self, + self._widget_list, + self._activity_root) + self._pages.append(page_controller) + self._pages.sort(cmp=KandidController.numeric_compare) + for page_controller in self._pages: + self._widget._notebook.append_page(*page_controller.create_gui()) + page_controller.autoconnect_events() + page_controller.localize() + page_controller.activate_gui() - def on_awfull_activate(self, *args): - """Set last ranking for this protozoon. - pre: len(args) >= 1 + def find_page(self, page_name): """ -# ka_debug.info('on_awfull_activate [%s]' % args[0].get_name()) - index = name_to_index(args[0].get_name()) - self.model.reduce_fitness(index) - self._update_population_gui() - ka_task.GeneratorTask(self.task_breed_single, - self.on_model_completed, - args[0].get_name()).start(index) - - def on_page_show(self, *args): - """Show page from notebook. Page number is in args[0]. - pre: len(args) >= 1 + pre: page_name is not None and len(page_name) > 1 """ - ka_debug.info('on_page_show [%u]' % args[0]) - self._widget_tree.get_widget('kandidNotebook'). \ - set_current_page(args[0]) - - def on_received(self, code_type, code_element, nick): - """Update population or protozoon preview when received from others.""" - ka_debug.info('on_received: Received %u bytes, type: [%s], from: [%s]' % \ - (len(code_element), code_type, nick)) - if code_type == kandidtube.SEND_POPULATION: - self._status.set(ka_status.TOPIC_COLLABORATION, - ka_status.SUB_RECEIVED, - 'Population from ' + nick) - if self.is_overwrite_allowed(): - self._start_from_scratch = False - self._update_model(model_population.from_buffer(code_element)) - self.start_all_calculations() - else: - in_model = model_population.from_buffer(code_element) - max_fit, best_ix, second_ix = -1, -1, -1 - for index, fit in enumerate(in_model.fitness): - if fit > max_fit: - second_ix = best_ix - best_ix, max_fit = index, fit - if best_ix >= 0: - self.incoming.append_protozoon(in_model.protozoans[best_ix]) - if second_ix >= 0: - self.incoming.append_protozoon(in_model.protozoans[second_ix]) - if best_ix >= 0 or second_ix >= 0: - ka_debug.info("I've already an evolved population, proposing protozoon %d, %d." % (best_ix, second_ix)) - else: - ka_debug.info("I've already an evolved population, ignore incoming protozoon.") - elif code_type == kandidtube.SEND_PROTOZOON: - ka_debug.info('Proposing protozoon.') - self._status.set(ka_status.TOPIC_COLLABORATION, - ka_status.SUB_RECEIVED, - 'Protozoon from ' + nick) - self.incoming.append_protozoon(model_population.from_buffer(code_element)) - else: - ka_debug.err('Somebody called me using an illegal type [%s]' - % code_type) - - def on_new_tube(self, telepathy_conn, tube, my_id, is_initiator, get_buddy): - """Creates communication object and sends population - pre: tube > 0 - pre: get_buddy is not None + for page_controller in self._pages: + pattern1 = "<class 'ep_page_" + pattern2 = "." + page_name + "'>" + type_str = str(type(page_controller)) + if type_str.startswith(pattern1) and type_str.endswith(pattern2): + return page_controller + ka_debug.err('Missing ' + page_name) + return None + + def find_page_number(self, page_name): """ - self._tube = kandidtube.KandidTube(self, telepathy_conn, tube, my_id, - is_initiator, get_buddy) - -# Event handling for incoming protozoans - def on_incoming_popup(self, widget, event): - ka_debug.info('on_incoming_popup: ' + widget.name) - index = name_to_index(widget.name) - self._show_popup(widget, event, 'incoming_menu_'+ str(index)) - - def _show_popup(self, widget, event, menu): -# ka_debug.info('%s [%s]' % (menu, widget.name)) - index = name_to_index(menu) - self._widget_tree.get_widget('favorite_menuitem_' + str(index)). \ - set_sensitive(self.model.fitness[index] < 9.0) - dummy, moderate, dummy = self.model.classify() - is_sensitive = len(moderate) > 0 - self._widget_tree.get_widget('awfull_menuitem_' + str(index)). \ - set_sensitive(is_sensitive) - popup_menu = self._widget_tree.get_widget(menu) - popup_menu.popup(None, None, None, event.button, event.time) - - def on_zoom_incoming(self, menu_item): -# ka_debug.info('on_zoom_incoming [%s]' % menu_item.parent.name) - if self._zoom_controller is not None: - index = name_to_index(menu_item.parent.name) - protozoon, dummy = self.incoming.at_index(index) - self._zoom_controller.start_calculation(protozoon) - - def on_accept_incoming(self, menu_item): - ka_debug.info('on_accept_incoming [%s]' % menu_item.parent.name) - protozoon = self.incoming.accept_protozoon( - name_to_index(menu_item.parent.name)) - if protozoon is not None: - new_at = self.model.replace(protozoon.copy()) - self.start_calculation([new_at]) - - def on_decline_incoming(self, menu_item): - ka_debug.info('on_decline_incoming [%s]' % menu_item.parent.name) - self.incoming.decline_protozoon(name_to_index(menu_item.parent.name)) - -# data model and persistence - def is_overwrite_allowed(self): - """Preserve an already evolved population from over writing.""" - return self.model.is_overwrite_allowed() - - def serialize_model(self): - """Serialize population to a string buffer.""" - return model_population.to_buffer(self.model) - - def read_file(self, file_path): - """Delegate reading from journal to data model - pre: (file_path is not None) and (len(file_path) >= 1) + pre: page_name is not None and len(page_name) > 1 """ - self._start_from_scratch = False - self._update_model(model_population.read_file(file_path)) -# self.start_all_calculations() + for page_number, page_controller in enumerate(self._pages): + pattern1 = "<class 'ep_page_" + pattern2 = "." + page_name + "'>" + type_str = str(type(page_controller)) + if type_str.startswith(pattern1) and type_str.endswith(pattern2): + return page_number + return -1 + + + def switch_page(self, page_name): + """ + pre: page_name is not None and len(page_name) > 1 + pre: self.find_page(page_name) is not None + """ + for page_number, page_controller in enumerate(self._pages): + pattern1 = "<class 'ep_page_" + pattern2 = "." + page_name + "'>" + type_str = str(type(page_controller)) + if type_str.startswith(pattern1) and type_str.endswith(pattern2): + self._widget_list.get_widget('kandidNotebook'). \ + set_current_page(page_number) + return - def write_file(self, file_path): - """Delegate writing data model to journal - pre: (file_path is not None) and (len(file_path) >= 1) - """ - model_population.write_file(file_path, self.model) - def close(self): """Clean up on close""" for page in self._pages: @@ -588,6 +116,7 @@ def name_to_index(name): def create_context(widget): """ Create cairo context for widget. pre: widget is not None + pre: widget.window is not None """ ctx = widget.window.cairo_create() ctx.rectangle(0, 0, \ |