# Sketch - A Python-based interactive drawing program # Copyright (C) 1997, 1998 by Bernhard Herzog # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # color gradient classes # from blend import Blend, MismatchError class Gradient: def __init__(self, duplicate = None): pass def ColorAt(self, pos): pass def Duplicate(self): return self.__class__(duplicate = self) class MultiGradient(Gradient): def __init__(self, colors = (), duplicate = None): if duplicate is not None: self.colors = duplicate.colors[:] else: if len(colors) < 2: raise ValueError, 'at least 2 colors required' self.colors = colors def StartColor(self): return self.colors[0][-1] def SetStartColor(self, color): undo = (self.SetStartColor, self.colors[0][-1]) self.colors[0] = (0, color) return undo def EndColor(self): return self.colors[-1][-1] def SetEndColor(self, color): undo = (self.SetEndColor, self.colors[-1][-1]) self.colors[-1] = (1, color) return undo def ColorAt(self, pos): colors = self.colors for i in range(len(colors) - 1): if colors[i][0] <= pos and colors[i + 1][0] >= pos: break else: return self.EndColor() start_pos, start_color = colors[i] if i < len(colors) - 1: end_pos, end_color = colors[i + 1] else: return start_color return Blend(end_color, start_color, (pos - start_pos) / float(end_pos - start_pos)) def Sample(self, num): colors = self.colors max = num - 1.0 pos1, color1 = colors[0] pos2, color2 = colors[1] diff = float(pos2 - pos1) cur = 1 result = [] blend = color1.Blend for i in range(num): frac = i / max while frac > pos2: pos1 = pos2; color1 = color2 cur = cur + 1 pos2, color2 = colors[cur] diff = float(pos2 - pos1) blend = color1.Blend frac = (frac - pos1) / diff result.append(blend(color2, 1 - frac, frac)) return result def Colors(self): return self.colors def SetColors(self): undo = (self.SetColors, self.colors) self.colors = colors return undo def Blend(self, other, frac1, frac2): if type(other) == type(self.colors[0][-1]): # blend a gradient with a single color sc = self.colors c = [] for i in range(len(sc)): p1, c1 = sc[i] c.append((p1, c1.Blend(other, frac1, frac2))) return self.__class__(c) elif other.__class__ == self.__class__: # blend two MultiGradient instances # XXX: improve this... length = min(len(self.colors), len(other.colors)) sc = self.colors[:length - 1] sc.append(self.colors[-1]) oc = other.colors[:length - 1] oc.append(other.colors[-1]) c = [] for i in range(length): p1, c1 = sc[i] p2, c2 = oc[i] c.append((frac1 * p1 + frac2 * p2, Blend(c1, c2, frac1, frac2))) return self.__class__(c) else: raise MismatchError def SaveToFile(self, file): file.Gradient(self.colors) # convenience function for the most common gradient type def CreateSimpleGradient(start, end): return MultiGradient([(0, start), (1, end)])