# 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 math import ka_debug import model_random import ka_factory import model_locus import model_layer import model_constraintpool import exon_position import exon_color from gettext import gettext as _ ORDER_CONSTRAINT = 'orderconstaint' SAMPLERTYPE_CONSTRAINT = 'samplertypeconstraint' STAMPTYPE_CONSTRAINT = 'stamptypeconstraint' NUMBER_OF_SITES_CONSTRAINT = 'sitenumberofconstraint' class VoronoiDiagramLayer(model_layer.Layer): """VoronoiDiagramLayer inv: len(self.sites_point) > 0 inv: len(self.sites_color) > 0 inv: self.sampler is not None inv: self.stamp is not None """ cdef = [{'bind' : ORDER_CONSTRAINT, 'name' : 'Natural logarithm of order p used in Minkowski distance', 'domain': model_constraintpool.FLOAT_RANGE, 'min' : -4.0, 'max': 4.0}, {'bind' : NUMBER_OF_SITES_CONSTRAINT, 'name' : 'Number of site points', 'domain': model_constraintpool.INT_RANGE, 'min' : 2, 'max': 10}, {'bind' : SAMPLERTYPE_CONSTRAINT, 'name' : 'Permitted sampler types', 'domain': model_constraintpool.STRING_1_OF_N, 'enum' : ka_factory.get_factory('sampler').keys()}, {'bind' : STAMPTYPE_CONSTRAINT, 'name' : 'Permitted stamp types', 'domain': model_constraintpool.STRING_1_OF_N, 'enum' : ka_factory.get_factory('stamp').keys()}, ] def __init__(self, trunk): """Voronoi diagram layer constructor""" super(VoronoiDiagramLayer, self).__init__(trunk) self.sites_point = [ exon_position.Position(self.path, 0.0, 0.0) ] self.sites_color = [ exon_color.Color(self.path, 0.0, 0.0, 0.0, 0.0) ] # self._distance = VoronoiDiagramLayer._euclidean_square_distance self.order = 2 # euclidean distance sampler_factory = ka_factory.get_factory('sampler') sampler_key = sampler_factory.keys()[0] self.sampler = sampler_factory.create(sampler_key, self.path) stamp_factory = ka_factory.get_factory('stamp') stamp_key = stamp_factory.keys()[0] self.stamp = stamp_factory.create(stamp_key, self.path) def dot(self): result = "" anchor = ka_debug.dot_id(self) + ' -> ' for ref in self.sites_point: result += ka_debug.dot_ref(anchor, ref) for ref in self.sites_color: result += ka_debug.dot_ref(anchor, ref) for ref in [self.sampler, self.stamp]: result += ka_debug.dot_ref(anchor, ref) return result def __eq__(self, other): """Equality """ equal = isinstance(other, VoronoiDiagramLayer) \ and model_layer.Layer.__eq__(self, other) \ and len(self.sites_point) == len(other.sites_point) \ and len(self.sites_color) == len(other.sites_color) \ and self.order == other.order \ and self.sampler == other.sampler \ and self.stamp == other.stamp if equal: for index, site_point in enumerate(self.sites_point): equal = equal and site_point == other.sites_point[index] if equal: for index, site_color in enumerate(self.sites_color): equal = equal and site_color == other.sites_color[index] return equal def randomize(self): """Randomize the layers components.""" super(VoronoiDiagramLayer, self).randomize() cpool = model_constraintpool.ConstraintPool.get_pool() number_of_constraint = cpool.get(self, NUMBER_OF_SITES_CONSTRAINT) order_constraint = cpool.get(self, ORDER_CONSTRAINT) self.order = model_random.uniform_constrained(order_constraint) sampler_factory = ka_factory.get_factory('sampler') samplertype_constraint = cpool.get(self, SAMPLERTYPE_CONSTRAINT) self.sampler = sampler_factory.create_random(samplertype_constraint, self.path) self.sampler.randomize() stamp_factory = ka_factory.get_factory('stamp') stamptype_constraint = cpool.get(self, STAMPTYPE_CONSTRAINT) self.stamp = stamp_factory.create_random(stamptype_constraint, self.path) self.stamp.randomize() # self.sites = [] # for dummy in range(model_random.randint_constrained(number_of_constraint)): # site_point = exon_position.Position(self.path, 0.0, 0.0) # site_point.randomize() # site_color = exon_color.Color(self.path, 0.0, 0.0, 0.0, 0.0) # site_color.randomize() # self.sites.append( (site_point, site_color) ) self.sites_point = [] for dummy in range(model_random.randint_constrained(number_of_constraint)): site_point = exon_position.Position(self.path, 0.0, 0.0) site_point.randomize() self.sites_point.append(site_point) self.sites_color = [] for dummy in range(model_random.randint_constrained(number_of_constraint)): site_color = exon_color.Color(self.path, 0.0, 0.0, 0.0, 0.0) site_color.randomize() self.sites_color.append(site_color) def mutate(self): """Make small random changes to the layers components.""" super(VoronoiDiagramLayer, self).mutate() cpool = model_constraintpool.ConstraintPool.get_pool() number_of_constraint = cpool.get(self, NUMBER_OF_SITES_CONSTRAINT) site_point = exon_position.Position(self.path, 0.0, 0.0) site_point.randomize() model_random.mutate_list(self.sites_point, number_of_constraint, site_point) site_color = exon_color.Color(self.path, 0.0, 0.0, 0.0, 0.0) site_color.randomize() model_random.mutate_list(self.sites_color, number_of_constraint, site_color) order_constraint = cpool.get(self, ORDER_CONSTRAINT) self.order = model_random.jitter_constrained(self.order, order_constraint) self.sampler.mutate() self.stamp.mutate() def swap_places(self): """Shuffle similar components.""" model_random.swap_places(self.sites_point) model_random.swap_places(self.sites_color) self.sampler.swap_places() self.stamp.swap_places() def crossingover(self, other): """ pre: isinstance(other, VoronoiDiagramLayer) pre: isinstance(self, VoronoiDiagramLayer) # check for distinct references, needs to copy content, not references post: __return__ is not self post: __return__ is not other post: model_locus.unique_check(__return__, self, other) == '' """ new_one = VoronoiDiagramLayer(self.get_trunk()) new_one.sites_point = model_random.crossingover_list(self.sites_point, other.sites_point) new_one.sites_color = model_random.crossingover_list(self.sites_color, other.sites_color) cross_sequence = model_random.crossing_sequence(1) new_one.order = self.order if cross_sequence[0] else other.order new_one.sampler = model_random.crossingover_elem(self.sampler, other.sampler) new_one.stamp = model_random.crossingover_elem(self.stamp, other.stamp) return new_one def render(self, ctx, width, height): """ pre: ctx is not None pre: width > 0 pre: height > 0 pre: width == height """ self.begin_render(ctx, width, height) for point in self.sampler.get_sample_points(): rgba = self._site_color_min_dist(point).rgba ctx.set_source_rgba(rgba[0], rgba[1], rgba[2], rgba[3]) self.stamp.render(ctx, (point[0]-0.5, point[1]-0.5) ) # data = array.array('B', chr(0) * width * height * 4) # stride = width * 4 # dx = 1.0 / width # dy = 1.0 / height # py = 0.0 # for iy in xrange(0, height*stride, stride): # px = 0.0 # for ix in xrange(0, width*4, 4): # ii = ix+iy # color = self._site_color_min_dist(px, py) # data[ii] = int(color.rgba[0] * 255.0) # blue # data[ii+1] = int(color.rgba[1] * 255.0) # green # data[ii+2] = int(color.rgba[2] * 255.0) # red # data[ii+3] = int(color.rgba[3] * 255.0) # alpha # px += dx # py += dy # for dy in xrange(0, height*stride, stride): # for dx in xrange(0, width*4, 4): # dd = dx+dy # data[dd] = 0 # blue # data[dd+1] = 255 # green # data[dd+2] = 0 # red # data[dd+3] = 64 # alpha # for dy in xrange(0, height*stride, stride): # dd = (width-1)*4+dy # data[dd] = 0 # blue # data[dd+1] = 0 # green # data[dd+2] = 255 # red # data[dd+3] = 255 # alpha # for dx in xrange(0, width*4, 4): # dd = dx+(height-1)*stride # data[dd] = 255 # blue # data[dd+1] = 0 # green # data[dd+2] = 0 # red # data[dd+3] = 255 # alpha # surface = cairo.ImageSurface.create_for_data( # data, cairo.FORMAT_ARGB32, width, height, stride) # ctx.scale(1.0/width, 1.0/height) # ctx.set_source_surface(surface) # ctx.paint() def _site_color_min_dist(self, point): """ Minkowski distance see http://en.wikipedia.org/wiki/Minkowski_distance pre: len(point) == 2 """ min_distance, at_index = 999999.9, 0 for index, site_point in enumerate(self.sites_point): # distance = self._distance(point, site[0]) p = math.exp(self.order) try: distance = (math.fabs(point[0]-site_point.x_pos)**p + math.fabs(point[1]-site_point.y_pos)**p) ** (1.0/p) if(distance < min_distance): min_distance, at_index = distance, index except OverflowError: # import sys # print OverflowError, sys.exc_info()[0], \ # '_site_color_min_dist', self.order, p, \ # point[0], site_point.x_pos, point[1], site_point.y_pos pass return self.sites_color[at_index % len(self.sites_color)] # @staticmethod # def _euclidean_square_distance(point, site): # """ # Like Euclidean distance distance but with square root. # see http://en.wikipedia.org/wiki/Euclidean_distance # x-coordinate is stored at index [0]. # y-coordinate is stored at index [1]. # """ # return (point[0]-site.x_pos)**2 + (point[1]-site.y_pos)**2 # # @staticmethod # def _manhattan_distance(point, site): # """ Taxicab distance, Manhattan distance) # see http://en.wikipedia.org/wiki/Manhattan_distance # x-coordinate is stored at index [0]. # y-coordinate is stored at index [1]. # """ # return math.fabs(point[0]-site.x_pos) + math.fabs(point[1]-site.y_pos) # # @staticmethod # def _chebyshev_distance(point, site): # """ Chebyshev distance # see http://en.wikipedia.org/wiki/Chebyshev_distance # x-coordinate is stored at index [0]. # y-coordinate is stored at index [1]. # """ # dx, dy = math.fabs(point[0]-site.x_pos), math.fabs(point[1]-site.y_pos) # return dx if dx > dy else dy def explain(self, formater): super(VoronoiDiagramLayer, self).explain(formater) formater.text_item('Natural logarithm of order p used in Minkowski distance: ' + str(self.order)) formater.position_array(self.sites_point, 'center points for sites:') formater.color_array(self.sites_color, 'site colors:') text, surface, descr = self.sampler.explain() if surface is not None: formater.surface_item(surface, 'sampling points: ' + text, descr) else: formater.text_item(text) text, surface, descr = self.stamp.explain() if surface is not None: formater.surface_item(surface, 'stamp: ' + text, descr) else: formater.text_item(text) def copy(self): """The Voronoi diagram layers copy constructor. # check for distinct references, needs to copy content, not references post: __return__ is not self """ new_one = VoronoiDiagramLayer(self.get_trunk()) self.copy_base(new_one) new_one.sites_point = model_random.copy_list(self.sites_point) new_one.sites_color = model_random.copy_list(self.sites_color) new_one.order = self.order new_one.sampler = self.sampler.copy() new_one.stamp = self.stamp.copy() return new_one