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...