diff options
Diffstat (limited to 'model_protozoon.py')
-rw-r--r-- | model_protozoon.py | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/model_protozoon.py b/model_protozoon.py new file mode 100644 index 0000000..2e389f0 --- /dev/null +++ b/model_protozoon.py @@ -0,0 +1,315 @@ +# coding: UTF8 +# Copyright 2009 Thomas Jourdan +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import random +import cairo +import ka_factory +import ka_random +import model_constraintpool +import exon_color +import model_allele + +TRUNK = '/' + +LAYERTYPE_CONSTRAINT = 'layertypeconstraint' +NUMBER_OF_LAYERS_CONSTRAINT = 'layernumberofconstraint' +MERGER_CONSTRAINT = 'mergertypeconstraint' + +class Protozoon(model_allele.Allele): + """ + inv: self.layers is not None + inv: self.mergers is not None + inv: self.background is not None + inv: isinstance(self.background, exon_color.Color) + """ + + cdef = [{'bind' : LAYERTYPE_CONSTRAINT, + 'name' : 'Permitted layer types', + 'domain': model_constraintpool.STRING_M_OF_N, + 'enum' : ka_factory.get_factory('layer').keys() + }, + {'bind' : NUMBER_OF_LAYERS_CONSTRAINT, + 'name' : 'Number of layers', + 'domain': model_constraintpool.INT_RANGE, + 'min' : 2, 'max': 5, + }, + {'bind' : MERGER_CONSTRAINT, + 'name' : 'Permitted merging strategies for layers', + 'domain': model_constraintpool.STRING_M_OF_N, + 'enum' : ka_factory.get_factory('merger').keys() + }, + ] + + def __init__(self): + super(Protozoon, self).__init__(TRUNK) + self.layers = [] + self.mergers = [] + self.background = exon_color.Color(self.path, 0, 0, 0, 1) + + def __eq__(self, other): + """Equality based on layers quantity and content.""" + equal = isinstance(other, Protozoon) \ + and len(self.layers) == len(other.layers) \ + and len(self.mergers) == len(other.mergers) \ + and self.background == other.background + if equal: + for index in range(len(self.layers)): + equal = equal and self.layers[index] == other.layers[index] + if equal: + for index in range(len(self.mergers)): + equal = equal and self.mergers[index] == other.mergers[index] + return equal + + def randomize(self): + """Randomize the protozoons components. + post: len(self.layers) >= 1 + post: len(self.mergers) >= 1 + """ + cpool = model_constraintpool.ConstraintPool.get_pool() + + # create layers + layer_factory = ka_factory.get_factory('layer') + layertype_constraint = cpool.get(self, LAYERTYPE_CONSTRAINT) + number_of_constraint = cpool.get(self, NUMBER_OF_LAYERS_CONSTRAINT) + + number_of_layers = random.randint(number_of_constraint[0], \ + number_of_constraint[1]) + self.layers = [] + for i in range(number_of_layers): + single_layer = layer_factory.create_random(layertype_constraint, self.path) + single_layer.randomize() + self.layers.append(single_layer) + + # create strategy for merging layers + merger_factory = ka_factory.get_factory('merger') + mergertype_constraint = cpool.get(self, MERGER_CONSTRAINT) + number_of_mergers = 1 if number_of_layers < 3 else int(number_of_layers / 2) + self.mergers = [] + for i in range(number_of_mergers): + single_merger = merger_factory.create_random(mergertype_constraint, self.path) + single_merger.randomize() + self.mergers.append(single_merger) + + # create background color + self.background.randomize() + + + def mutate(self): + """Make small random changes to the protozoon. + """ + cpool = model_constraintpool.ConstraintPool.get_pool() + layertype_constraint = cpool.get(self, LAYERTYPE_CONSTRAINT) + number_of_constraint = cpool.get(self, NUMBER_OF_LAYERS_CONSTRAINT) + + self._mutate_list(self.layers, + layertype_constraint, number_of_constraint, + ka_factory.get_factory('layer'), self.path) + mergertype_constraint = cpool.get(self, MERGER_CONSTRAINT) + self._mutate_list(self.mergers, + mergertype_constraint, number_of_constraint, + ka_factory.get_factory('merger'), self.path) + + # mutate background color + self.background.mutate() + + def shuffle(self): + """Shuffle layer list and delegate shuffling to the protozoons componets.""" + # delegate shuffling to the layers child components + for single_layer in self.layers: + single_layer.shuffle() + # shuffle layers + if ka_random.is_shuffling(): + random.shuffle(self.layers) + # shuffle mergers + if ka_random.is_shuffling(): + random.shuffle(self.mergers) + + def crossingover(self, other): + """Returns a deep copy mixed from my protozoon and the other protozoon. + pre: isinstance(other, Protozoon) + """ + # deep copy + new_one = Protozoon() + # crossing over the layers + for single_layer in self._crossingover_list(self.layers, other.layers): + new_one.layers.append(single_layer.copy()) + + # crossing over the layer mergers + new_one.mergers = [m.copy() for m in self._crossingover_list(self.mergers, + other.mergers)] + # crossing over the background color + new_one.background = self.background.crossingover(other.background) + return new_one + + def _crossingover_list(self, this_list, other_list): + # crossing over common part of this list and other list + len_this, len_other = len(this_list), len(other_list) + min_elements = min([len_this, len_other]) + max_elements = max([len_this, len_other]) + + new_list = [] + cross_sequence = ka_random.crossing_sequence(min_elements) + for index in range(min_elements): + this_element, other_element = this_list[index], other_list[index] + if this_element.__class__ == other_element.__class__: + # delegate crossing over to the elements components + new_list.append(this_element.crossingover(other_element)) + else: + # crossing over whole elements + if cross_sequence[index]: + new_list.append(this_element) + else: + new_list.append(other_element) + + # appending elements from protozoon of greater size + if len_this > len_other: + for index in range(min_elements, max_elements): + new_list.append(this_list[index]) + if len_this < len_other: + for index in range(min_elements, max_elements): + new_list.append(other_list[index]) + return new_list + + def _mutate_list(self, this_list, type_constraint, number_of_constraint, + factory, path): + # maybe remove one element + len_this = len(this_list) + if len_this > number_of_constraint[0] and ka_random.is_mutating(): + del this_list[random.randint(0, len_this-1)] + len_this -= 1 + # maybe duplicate one of the elements + if len_this < number_of_constraint[1] and ka_random.is_mutating(): + random_element = this_list[random.randint(0, len_this-1)] + dupli_element = random_element.copy() + this_list.insert(random.randint(0, len_this-1), dupli_element) + len_this += 1 + # maybe insert a new element + if len_this < number_of_constraint[1] and ka_random.is_mutating(): + single_element = factory.create_random(type_constraint, path) + single_element.randomize() + this_list.insert(random.randint(0, len_this-1), single_element) + len_this += 1 + + # delegate mutation to the elements child components + for single_element in this_list: + single_element.mutate() + + def draw(self, ctx, width, height): + """ + pre: ctx is not None + pre: width > 0 + pre: height > 0 + pre: width == height + """ + ctx.save() + ctx.scale(width, height) + ctx.translate(0.5, 0.5) + + # paint background + ctx.set_operator(cairo.OPERATOR_SOURCE) + ctx.set_source_rgb(self.background.rgba[0], + self.background.rgba[1], + self.background.rgba[2]) + ctx.paint() + + # merge all layers + for index, single_layer in enumerate(self.layers): + self.mergers[index % len(self.mergers)].draw_single_layer( + single_layer, + self.layers[-1-index], + ctx, width, height) + ctx.restore() + + def explain(self, formater): + width = height = 256 + # begin with header + titel = 'protozoon ' + str(id(self)) + formater.header(titel) + + # display combined layers + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + ctx = cairo.Context(surface) + self.draw(ctx, width, height) + formater.surface_item(surface, 'combined layers', titel) + + # explain all layers from top to bottom + formater.begin_list('protozoon has %d layers' % (len(self.layers))) + reverse_list = self.layers[:] + reverse_list.reverse() + for index, single_layer in enumerate(reverse_list): + title = 'top level layer' if index == 0 else 'layer ' + str(index) + formater.begin_list(title) + # explain operator to merge layer + super(single_layer.__class__, single_layer).explain(formater) + + # explain layer merging strategy + text, surface, descr = self.mergers[index % len(self.mergers)].explain() + if surface is not None: + formater.surface_item(surface, 'merging strategy:' + text, descr) + else: + formater.text_item(text) + + # preview of a single layer + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + ctx = cairo.Context(surface) + ctx.scale(width, height) + ctx.translate(0.5, 0.5) + + # paint checker board background + steps = 16 +# delta = (1.0 * width) / (1.0 * steps) + delta = (1.0) / (1.0 * steps) + ctx.set_operator(cairo.OPERATOR_SOURCE) + for row in range(steps): + for col in range(steps): + ctx.rectangle(col * delta - 0.5, row * delta - 0.5, + delta, delta) + if (col + row) % 2 == 0: + ctx.set_source_rgb(0.4, 0.4, 0.4) + else: + ctx.set_source_rgb(0.6, 0.6, 0.6) + ctx.fill() + + ctx.save() + single_layer.draw(ctx, width, height) + formater.surface_item(surface, single_layer.__class__.__name__, \ + single_layer.__class__.__name__) + ctx.restore() + + # explain the layers details + formater.begin_list('Details for ' + single_layer.__class__.__name__) + single_layer.explain(formater) + formater.end_list() + formater.end_list() + formater.end_list() + + #explain background color + formater.color_item(self.background, 'background color:', alfa=False) + + # stop with footer + formater.footer() + + def copy(self): + """ The protozoons copy constructor. + """ + new_one = Protozoon() + new_one.layers = [] + for single_layer in self.layers: + new_one.layers.append(single_layer.copy()) + new_one.mergers = [m.copy() for m in self.mergers] + new_one.background = self.background.copy() + return new_one |