Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/model_protozoon.py
diff options
context:
space:
mode:
Diffstat (limited to 'model_protozoon.py')
-rw-r--r--model_protozoon.py315
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