2""" @brief  Stores data that can be used to plot curves and stored into a file.
 
    5#============================================================================# 
    6# EVOLIFE  http://evolife.telecom-paris.fr             Jean-Louis Dessalles   
   23if __name__ == 
'__main__':  sys.path.append(
'../..')  
 
   25from functools 
import reduce
 
   33EvolifeColours = [
'#808080', 
'#000000', 
'#FFFFFF', 
'#0000FF', 
'#FF0000', 
'#FFFF00', 
'#A06000', 
'#0080A0', 
'#FF80A0', 
'#94DCDC', 
 
   34            '#008000', 
'#009500', 
'#00AA00', 
'#00BF00', 
'#00D400', 
'#00E900', 
'#00FE00', 
'#64FF64', 
'#78FF78', 
'#8CFF8C', 
'#A0FFA0', 
'#B4FFB4',
 
   35            '#800000', 
'#950000', 
'#AA0000', 
'#BF0000', 
'#D40000', 
'#E90000', 
'#FE0000', 
'#FF6464', 
'#FF7878', 
'#FF8C8C', 
'#FFA0A0', 
'#FFB4B4',
 
   36            '#000080', 
'#101095', 
'#2020AA', 
'#3030BF', 
'#3838D4', 
'#4747E9', 
'#5555FE', 
'#6464FF', 
'#7878FF', 
'#8C8CFF', 
'#A0A0FF', 
'#B4B4FF',
 
   38EvolifeColourNames = [
'grey', 
'black', 
'white', 
'blue', 
'red', 
'yellow', 
'brown', 
'blue02', 
'pink', 
'lightblue', 
 
   39            'green', 
'green1', 
'green2', 
'green3', 
'green4', 
'green5', 
'green6', 
'green7', 
'green8', 
'green9', 
'green10', 
'green11',  
 
   40            'red0', 
'red1', 
'red2', 
'red3', 
'red4', 
'red5', 
'red6', 
'red7', 
'red8', 
'red9', 
'red10', 
'red11', 
 
   41            'blue0', 
'blue1', 
'blue2', 
'blue3', 
'blue4', 
'blue5', 
'blue6', 
'blue7', 
'blue8', 
'blue9', 
'blue10', 
'blue11',
 
   44Shades = {
'green':(10, 21), 
'red':(22,33), 
'blue':(34, 45)}
 
   46def Shade(x, BaseColour='green', Min=0, Max=1, darkToLight=True, invisible='white'):
 
   47    """ compute a shade for a given base colour  
   48        Green colours between 10 and 21
 
   49        Red colours between 22 
and 33
 
   50        Blue colours between 34 
and 45
 
   53    Frame = lambda x, Range, Min, Max: int(((x - Min) * Range) / (Max - Min)) 
   55    if Min != Max and x >= Min and x <= Max: 
   56        if BaseColour == 
'grey': 
 
   58                return '#' + (
'%02X' % Frame(x, 255, Min, Max)) * 3 
 
   60                return '#' + (
'%02X' % Frame(Max-x, 255, Min, Max)) * 3 
 
   61        shades = Shades[BaseColour]
 
   63            return EvolifeColourNames[shades[0] + Frame(x, (shades[1] - shades[0]), Min, Max)]
 
   67            return EvolifeColourNames[shades[0] + Frame(Max - x, (shades[1] - shades[0]), Min, Max)]
 
   71    """ Recognizes Colour_designation as a number, a name, a (R,V,B) tuple or a #RRVVBB pattern. 
   72        Returns the recognized colour as a couple (Number, Name)
 
   76        if str(Colour_designation).isdigit() 
and int(Colour_designation) 
in range(len(EvolifeColours)): 
 
   77            ID = int(Colour_designation)
 
   78            return (ID, EvolifeColours[ID])
 
   79        elif Colour_designation 
in EvolifeColourNames:  
 
   80            ID = EvolifeColourNames.index(Colour_designation)
 
   81            return (ID, EvolifeColours[ID])     
 
   82        ColourCode =  Colour_designation    
 
   83        if isinstance(Colour_designation, tuple):
 
   84            ColourCode = 
'#%02X%02X%02X' % Colour_designation
 
   85        if ColourCode 
in EvolifeColours:    
 
   86            ID = EvolifeColours.index(ColourCode)
 
   87            return (ID, EvolifeColours[ID])
 
   88        return (0, ColourCode)  
 
   89    except (AttributeError, TypeError): 
 
   90        print(
'colour error', Colour_designation)
 
   99    """ stores coordinates as: (x, y, colour, size) 
  100        Missing values are completed with default values
 
  101        A fractional size value means a fraction of the reference size (typically window width)
 
  102        A negative size value means that size 
is provided 
in logical coordinates 
 
  103        (
and that the object should be resized when zoomed). Otherwise size means pixels.
 
  106    def __init__(self, Coordinates, RefSize=None): 
  107        """ A Stroke is a point or shape. 
  108            It is represented by a tuple (x, y, colour, size).
 
  109            Missing values are completed by default values.
 
  110            A fractional size value means a fraction of the reference size (typically window width)
 
  111            A negative size value means that size 
is provided 
in logical coordinates 
 
  112            (
and that the object should be resized when zoomed). Otherwise size means pixels.
 
  117        DefaultStroke = (DefCoord, DefCoord, DefColour, DefSize)
 
  120            self.
Coord = Coordinates[:4] + DefaultStroke[min(len(Coordinates), 4):4] 
 
  121            (self.x, self.y, self.colour, self.
size) = self.
Coord 
  122            if RefSize 
and abs(self.
size) < 1:  
 
  129            self.
Coord = (self.x, self.y, self.colour, self.
size)
 
  132            (self.x, self.y, self.colour, self.
size) = 0,0,0,0
 
  137        return (self.x, self.y)
 
  140        """ coordinates + size 
  142        return (self.x + self.
size, self.y + self.
size)
 
  146        C1 = list(self.
Coord)
 
  148        self.
Coord = tuple(C1)
 
  151        if Other.Coord: 
return self.
Coord + Other.Coord
 
  152        else:       
return self.
Coord 
  154    def __str__(self):  
return '%s, %s, %s, %s' % (str(self.x), str(self.y), str(self.colour), str(self.
size))
 
  160    """ Holds a complete (continuous) curve in memory 
  162    def __init__(self, colour, ID, ColName=None, Legend=None):
 
  163        """ creation of a curve. 
  164            A curve is a list of successive connected positions + a list of dicontinuities 
 
  170        except IndexError:  self.
ColName = colour
 
  171        if ColName 
is not None:  
 
  184        """ A curve is a list of successive connected positions + a list of dicontinuities  
  192        """ sets the curve's name  
  194        if N != 
"": self.
Name = N
 
  198        """ sets the curve's caption  
  204        """ returns the last position in the curve  
  208    def add(self, Pos, Draw=True):
 
  209        """ Adds a new position to the curve. 
  210            Notes a discontinuity if 'Draw' is False.
 
  221        """ list of x-coordinates  
  223        return tuple(map(
lambda P: P[0], self.
positions))
 
  226        """ list of y-coordinates  
  228        return tuple(map(
lambda P: round(P[1],3), self.
positions))
 
  231        """ compute average value of Y_coord  
  234        try:    ValidValues = [P[1] 
for P 
in self.
positions if P[0] >= start 
and P[1] >= 0]
 
  238            return int(round(float(sum(ValidValues)) / len(ValidValues)))
 
  247        """2.6-3.x version""" 
  251        """ Iteratively returns segments of the curve  
  266    def __str__(self):  
return self.Name
 
  274    """ Stores a list of 'Curves' 
  278        """ Creates a list of curves matching all available Evolife colours 
  281        self.Curves = [Curve(Colour, Number, EvolifeColourNames[Number]) for (Number,Colour) 
in enumerate(EvolifeColours)]
 
  286        """ defines where a curve should start 
  289            self.
Curves[Curve_id].start(location)
 
  291            error(
"Curves: unknown Curve ID")
 
  294        """ Adds a point to a Curve. Stores the Curve as "used" 
  299        self.
Curves[Curve_id].add(Point, Draw=Draw)
 
  302        """ records names for Curves. 
  303            Names = list of (Colour, Name, Legend) tuples (Name and Legend replaced by 
'' if missing)
 
  305        Str =  '\nDisplay: \n\t' 
  308            for Curve_description 
in Names:
 
  309                (Curve_designation, Name, Legend) = Curve_description + (0, 
'', 
'')[len(Curve_description):]
 
  314                        P.legend(Legend 
if Legend 
else Name)
 
  315                        Str += 
'\n\t%s:\t%s' % (P.ColName, P.legend())
 
  320            error(
"Curves: unknown Curve ID")
 
  324        """ returns actually used curves  
  330        """ returns tuples (ID, colour, colourname, curvename, legend) representing active curves 
  332        return [(P.ID, P.colour, P.ColName, P.Name, P.Legend) 
for P 
in self.
ActiveCurves()]
 
  334    def dump(self, ResultFileName=None, ResultHeader='', DumpStart=0):
 
  335        """ Saves Curves to a file. 
  336            Average values are stored in a file 
with '_res' appended to ResultFileName
 
  339        if ResultFileName == 
None:  
return {}
 
  343        X_coordinates = list(set([P.X_coord() 
for P 
in self.
Curves]))
 
  344        X_coordinates.sort(key=
lambda x: len(x), reverse=
True)
 
  345        if len(X_coordinates) <= 2:
 
  348            active_Curves = [P 
for P 
in self.
ActiveCurves() 
if P.X_coord() == X_coordinates[0]]
 
  350            Coords = [(
'Year',) + tuple([P.name() 
for P 
in active_Curves])]
 
  352                                 + [P.Y_coord() 
for P 
in active_Curves])
 
  355            Coords = reduce(
lambda x,y: x+y, [P.positions 
for P 
in self.
Curves 
  356                                              if len(P.positions) > 1]) 
 
  358        File_dump = open(ResultFileName + 
'.csv', 
'w')
 
  360            File_dump.write(
';'.join([str(x) 
for x 
in C]))
 
  361            File_dump.write(
'\n')
 
  366            HeaderLines = ResultHeader.split(
'\n')
 
  367            HeaderLines[0] += 
'LastStep;' 
  369            HeaderLines[0] += 
';'.join([P.name() 
for P 
in active_Curves])
 
  370            Header = 
'\n'.join(HeaderLines)
 
  375        try:    AvgStr += 
'%d;' % active_Curves[0].X_coord()[-1]    
 
  376        except IndexError:  
pass 
  377        AvgStr += 
';'.join([str(P.Avg(DumpStart)) 
for P 
in active_Curves])
 
  380        Averages = open(ResultFileName + 
'_res.csv', 
'w').write(AvgStr)
 
  384        try:    ResultDict[
'LastStep'] = str(active_Curves[0].X_coord()[-1])
 
  385        except IndexError:  ResultDict[
'LastStep'] = 0
 
  386        for P 
in active_Curves: ResultDict[P.name()] = str(P.Avg(DumpStart)) 
 
  391if __name__ == 
"__main__":
 
  395__author__ = 
'Dessalles' 
Holds a complete (continuous) curve in memory.
def __init__(self, colour, ID, ColName=None, Legend=None)
creation of a curve.
def Y_coord(self)
list of y-coordinates
def add(self, Pos, Draw=True)
Adds a new position to the curve.
def erase(self)
reset curve
def __next__(self)
2.6-3.x version
def Avg(self, start=0)
compute average value of Y_coord
def name(self, N="")
sets the curve's name
def legend(self, L="")
sets the curve's caption
def next(self)
Iteratively returns segments of the curve.
def X_coord(self)
list of x-coordinates
def last(self)
returns the last position in the curve
def start(self, StartPos)
A curve is a list of successive connected positions + a list of dicontinuities.
Stores a list of 'Curves'.
def dump(self, ResultFileName=None, ResultHeader='', DumpStart=0)
Saves Curves to a file.
def ActiveCurves(self)
returns actually used curves
def start_Curve(self, Curve_id, location)
defines where a curve should start
def __init__(self)
Creates a list of curves matching all available Evolife colours.
def Legend(self)
returns tuples (ID, colour, colourname, curvename, legend) representing active curves
def CurveAddPoint(self, Curve_id, Point, Draw=True)
Adds a point to a Curve.
def Curvenames(self, Names)
records names for Curves.
Stroke: drawing element (point or segment) #.
def endpoint(self)
coordinates + size
def point(self)
returns (x,y)
def EvolifeColourID(Colour_designation, default=(4, 'red'))
Recognizes Colour_designation as a number, a name, a (R,V,B) tuple or a #RRVVBB pattern.
def Shade(x, BaseColour='green', Min=0, Max=1, darkToLight=True, invisible='white')
compute a shade for a given base colour Green colours between 10 and 21 Red colours between 22 and 33...