All pastes #631618 Raw Edit

Something

public text v1 · immutable
#631618 ·published 2007-07-24 04:08 UTC
rendered paste body
import Image
import math


def angle_normalize(angle):
	return(angle % 360)   # uhh, DUH
	#while angle >= 360: angle -= 360
	#while angle < 0: angle += 360
	#return(angle)

# Fixme: add/multiply functions?
# in the interest of speed, we do not calculate HSV values at construction
class RGB:
	# Probably works best for r,g,b in [0,1]
	def __init__(self, r, g, b):
		self.r = r
		self.g = g
		self.b = b
	def blend(self, color2, amount):
		r = (1-amount)*self.r + amount*color2.r
		g = (1-amount)*self.g + amount*color2.g
		b = (1-amount)*self.b + amount*color2.b
	def toRGB(self): return self
	def __add__(self, color2):
		return RGB(self.r + color2.r, self.g + color2.g, self.b + color2.b)
	def __repr__(self):
		return "RGB<%0.2f %.2f %.2f>" % (self.r, self.g, self.b)
	def __sub__(self, color2):
		return RGB(self.r - color2.r, self.g - color2.g, self.b - color2.b)
	def __mul__(self, scalar):
		return RGB(self.r * scalar, self.g * scalar, self.b * scalar)
	def __div__(self, scalar):
		return RGB(self.r / scalar, self.g / scalar, self.b / scalar)
	def inverse(self):
		return RGB(1-self.r,1-self.g,1-self.b)
	# http://en.wikipedia.org/wiki/HSV_color_space
	def toHSV(self):
		max_c = max(self.r, self.g, self.b)
		min_c = min(self.r, self.g, self.b)
		if (self.r == max_c): h = 60.0*(self.g - self.b) / (max_c - min_c)
		elif (self.g == max_c): h =60.0*(2 + (self.b - self.r) / (max_c - min_c))
		elif (self.b == max_c): h = 60.0*(4 + (self.r - self.g) / (max_c - min_c))
		s = (max_c - min_c) / max_c
		v = max_c
		return HSV(h,s,v)
	def discrete_tuple(self, max_sample):
		return(int(self.r*max_sample), int(self.g*max_sample), int(self.b*max_sample))

# Fixme: add/multiply functions?
class HSV:
	def __init__(self, h, s, v):
		self.h = h
		self.s = s
		self.v = v
	# http://en.wikipedia.org/wiki/HSV_color_space
	# not quite as readable as my homebrew algorithm
	def toRGB(self):
		if (self.s == 0.0): return RGB(self.v, self.v, self.v)
		else:
			h = angle_normalize(self.h)
			h_i = math.floor(h / 60.0)
			f = h/60.0 - h_i
			p = self.v * (1 - self.s)
			q = self.v * (1 - f * self.s)
			t = self.v * (1 - (1 - f) * self.s)
			if h_i == 0: return RGB(self.v, t, p)
			elif h_i == 1: return RGB(q, self.v, p)
			elif h_i == 2: return RGB(p, self.v, t)
			elif h_i == 3: return RGB(p, q, self.v)
			elif h_i == 4: return RGB(t, p, self.v)
			elif h_i == 5: return RGB(self.v, p, q)
	def toHSV(self): return self
	def complement(self):
		return HSV(angle_normalize(180 + self.h), self.s, self.v)
	def __repr__(self):
		return "HSV<%ideg %.2f %.2f>" % (self.h, self.s, self.v)
	def __add__(self, color2):
		return (self.toRGB() + color2.toRGB()).toHSV()
	def __sub__(self, color2):
		return (self.toRGB() - color2.toRGB()).toHSV()
	def __mul__(self, scalar):
		return (self.toRGB() * scalar).toHSV()
	def __div__(self, scalar):
		return (self.toRGB() / scalar).toHSV()
	def inverse(self):
		return (self.toRGB().inverse()).toHSV()
	def hsv_add(self, color2):
		return HSV(angle_normalize(self.h + color2.h), self.s + color2.s, self.v + color2.v)
	def hsv_mult(self, scalar):
		return HSV(angle_normalize(self.h*scalar), self.s*scalar, self.v*scalar)
# 	Fixme - issues if it's blending, say, 0 and 359 degrees
#	def blend(self, color2, n):
#		return(HSV(self.h*(1-n) + color2.h*n, self



# (independent, dependent)
class Map:
	def __init__(self, start, end):
		self.slope = (float(end.y) - start.y) / (end.x - start.x)
		self.start = start
		self.end = end
	def fn(self, hue):
		return self.slope*(hue - start.x) + start.y

class Map2:
	def __init__(self, start_x, end_x, start_y, end_y):
		self.start_x = start_x
		self.end_x = end_x
		self.start_y = start_y
		self.end_y = end_y
		self.slope = (float(start_y - end_y) / (start_x - end_x))
	def fn(self, x):
		return self.slope * (x - self.start_x) + self.start_y

# This is so much more coherent....
# Define a map, add items with .add(x1, x2, y1, y2) - it will handle overlaps for you.
class MapConstruct:
	def __init__(self):
		self.maps = []
	def add(self, start_x, end_x, start_y, end_y):
		mapped = False
		for old_map in self.maps:
			# (1) Add the new map. It overlaps, but we're fixing that.
			# (2) Add another map which starts right where (1) ends, and
			# ends where the old map (the one we're on top of) ends.
			# (3) Change the old map underneath everything so that it ends
			# where (1) begins.
			if (start_x >= old_map.start_x) and (start_x < old_map.end_x):
				mapped = True
				self.maps.append(Map2(start_x, end_x, start_y, end_y))
				self.maps.append( Map2(end_x, old_map.end_x, old_map.fn(end_x), old_map.fn(old_map.end_x)) )
				old_map.end_x = start_x
				break
		if (not mapped): self.maps.append(Map2(start_x, end_x, start_y, end_y))
	def fn(self, x):
		found = False
		for map in self.maps:
			if (x >= map.start_x) and (x < map.end_x):
				return map.fn(x)
				break
		if(not found):
			print "Not found: %s" % x
			return 0

# For speed, these maps are very simple and not too tolerant of users trying to taunt them.
# If they overlap, it's anyone's guess which one wins.
class Hue_Map:
	def __init__(self, maps, default_hue = 0):
		self.maps = maps
		self.parts = len(maps)
		self.default_hue = default_hue   # shouldn't be needed...
	def fn(self, color):
		mapped = color.toHSV()
		mapped.h = angle_normalize(mapped.h)
		new_hue = self.default_hue
		for i in self.maps:
			if (mapped.h >= i.start.x) and (mapped.h < i.end.x):
				new_hue = i.slope*(mapped.h - i.start.x) + i.start.y
				break
		return HSV(new_hue, mapped.s, mapped.v)