#
# Copyright (c) 2002, 2003, 2004, 2005 Art Haas
#
# This file is part of PythonCAD.
#
# PythonCAD 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 2 of the License, or
# (at your option) any later version.
#
# PythonCAD 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 PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
# classes for colors
#
from PythonCAD.Generic import globals
#
# a seemingly good way to convert from string to integer
# for a couple of common ways of expressing colors ...
#
def color_str_to_int(cstr):
if cstr.startswith('0x') or cstr.startswith('0X'):
_val = int(cstr, 16)
elif cstr.startswith('#'):
_val = int(cstr[1:], 16)
else:
_val = int(cstr)
return _val
class Color(object):
"""An object representing an RGB color.
The class purpose is self evident.
A Color object has three attributes:
r: Red (0-255)
g: Green (0-255)
b: Blue (0-255)
There is no alpha-channel attribute (yet)...
A Color object has the following methods:
getRed(): Get the Red value in the Color.
getBlue(): Get the Blue value in the Color.
getGreen(): Get the Green value in the Color.
clone(): Return an identical copy of a Color.
Once a color object is created, the values it
contains may not be changed.
"""
def __init__(self, r=None, g=None, b=None):
"""Initialize a Color object.
There are several ways to create a color object:
Color(r,g,b) => r, g, and b values are all integers 0 <= value <= 255
Color(0xxxxx) => A hexidecimal value - it must begin with 0x. The color
is computed as follows:
r = (0xff0000 & value) >> 16
g = (0xff00 & value) >> 8
b = (0xff & value)
Color('#hexvalue') => A string prefixed with '#', with the remaining
characters representing a hexidecimal value.
Color() => A default color with r = g = b = 255.
"""
_r = r
_g = g
_b = b
if isinstance(_r, str):
_val = color_str_to_int(_r)
_r = (0xff0000 & _val) >> 16
_g = (0xff00 & _val) >> 8
_b = (0xff & _val)
elif isinstance(_r, int) and _g is None and _b is None:
_r = (0xff0000 & r) >> 16
_g = (0xff00 & r) >> 8
_b = (0xff & r)
elif (isinstance(_r, int) and
isinstance(_g, int) and
isinstance(_b, int)):
if _r < 0 or _r > 255:
raise ValueError, "Invalid Red value: %d" % _r
if _g < 0 or _g > 255:
raise ValueError, "Invalid Green value: %d" % _g
if _b < 0 or _b > 255:
raise ValueError, "Invalid Blue value: %d" % _b
elif _r is None and _g is None and _b is None:
_r = 255
_g = 255
_b = 255
else:
raise SyntaxError, "Invalid call to Color()."
self.__color = (_r << 16) | (_g << 8) | _b
def __eq__(self, obj):
"""Compare two Color objects for equivalence.
"""
if not isinstance(obj, Color):
return False
return self.getColors() == obj.getColors()
def __ne__(self, obj):
"""Compare two Color objects for equivalence.
"""
if not isinstance(obj, Color):
return True
return self.getColors() != obj.getColors()
def __cmp__(self, obj):
"""Compare two Color objects.
The comparison is done based on the RGB values.
red value of C1 < red value of C2 ==> return -1
red value of C1 > red value of C2 ==> return 1
Then green values are compared, then blue. If
all values are equal, return 0.
"""
if not isinstance(obj, Color):
raise TypeError, "Invalid object for color comparison: " + `obj`
_val = self.__color
_objval = hash(obj)
return cmp(_val, _objval)
def __hash__(self):
"""Return a hash value for the Color object.
Providing this method means that Color objects can be used
as keys in dictionaries.
"""
return self.__color
def __repr__(self):
_val = self.__color
_r = (_val & 0xff0000) >> 16
_g = (_val & 0xff00) >> 8
_b = (_val & 0xff)
return "Color(%d,%d,%d)" % (_r, _g, _b)
def __str__(self):
return "#%06x" % self.__color
def getColors(self):
"""Return a three-item tuple with the values comprising this color.
getColors()
"""
_val = self.__color
_r = (_val & 0xff0000) >> 16
_g = (_val & 0xff00) >> 8
_b = (_val & 0xff)
return _r, _g, _b
def getRed(self):
"""Return the red value of the color.
getRed()
"""
return (self.__color & 0xff0000) >> 16
r = property(getRed, None, None, "Red value of the color.")
def getGreen(self):
"""Return the green value of the color.
getGreen()
"""
return (self.__color & 0xff00) >> 8
g = property(getGreen, None, None, "Green value of the color.")
def getBlue(self):
"""Return the blue value of the color.
getBlue()
"""
return (self.__color & 0xff)
b = property(getBlue, None, None, "Blue value of the color.")
def clone(self):
"""Return a new Color object with the same color values.
clone()
"""
_val = self.__color
return Color(_val)
#
# ColorDict Class
#
# The ColorDict is built from the dict object. Using instances
# of this class will guarantee than only Color objects will be
# stored in the instance
#
class ColorDict(dict):
def __init__(self):
super(ColorDict, self).__init__()
def __setitem__(self, key, value):
if not isinstance(key, Color):
raise TypeError, "ColorDict keys must be color objects: " + `key`
if not isinstance(value, Color):
raise TypeError, "ColorDict values must be Color objects: " + `value`
super(ColorDict, self).__setitem__(key, value)
#
# find a Color object stored in the global color dictionary
# or make a new one and store it
#
def get_color(r, g, b):
if not isinstance(r, int):
raise TypeError, "Invalid red value:" + `r`
if r < 0 or r > 255:
raise ValueError, "Invalid red value: %d" % r
if not isinstance(g, int):
raise TypeError, "Invalid green value:" + `g`
if g < 0 or g > 255:
raise ValueError, "Invalid green value: %d" % g
if not isinstance(b, int):
raise TypeError, "Invalid blue value:" + `b`
if b < 0 or b > 255:
raise ValueError, "Invalid blue value: %d" % b
_color = Color(r, g, b)
if _color in globals.colors:
_color = globals.colors[_color]
else:
globals.colors[_color] = _color
return _color