aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Touhey <thomas@touhey.fr>2019-05-10 16:12:26 +0200
committerThomas Touhey <thomas@touhey.fr>2019-05-10 16:12:26 +0200
commitfe5f70464d1abf5a0c7d7decab1f376887e944f3 (patch)
treeb03ac1f44cee8e3b538d9c7548f993678b8eb522
parentee12fd3938d2fd30bdd61a29790c3bc823702c72 (diff)
Enhanced the docs.
-rw-r--r--docs/angles.rst12
-rw-r--r--docs/colors.rst11
-rw-r--r--docs/conf.py14
-rw-r--r--docs/index.rst27
-rw-r--r--setup.cfg2
-rwxr-xr-xthcolor/_angle.py107
-rwxr-xr-xthcolor/_color.py174
7 files changed, 279 insertions, 68 deletions
diff --git a/docs/angles.rst b/docs/angles.rst
new file mode 100644
index 0000000..cb63f14
--- /dev/null
+++ b/docs/angles.rst
@@ -0,0 +1,12 @@
+Managing angles
+===============
+
+Some color representations use angles as some of their properties. Angles can
+have one of the following types:
+
+.. autoclass:: thcolor.Angle.Type
+
+Angles in ``thcolor`` are instances of the following class:
+
+.. autoclass:: thcolor.Angle
+ :members: type, degrees, gradiants, radiants, turns
diff --git a/docs/colors.rst b/docs/colors.rst
new file mode 100644
index 0000000..a0ac8c8
--- /dev/null
+++ b/docs/colors.rst
@@ -0,0 +1,11 @@
+Managing colors
+===============
+
+Colors can have one of the following types:
+
+.. autoclass:: thcolor.Color.Type
+
+Colors are represented in ``thcolor`` as instances of the following class:
+
+.. autoclass:: thcolor.Color
+ :members: from_text, type, rgb, rgba, hls, hlsa, hwb, hwba, css
diff --git a/docs/conf.py b/docs/conf.py
index 91fe9c9..d48ebc8 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -9,11 +9,14 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
+def _add_paths():
+ import os
+ import sys
+
+ sys.path.insert(0, os.path.abspath('..'))
+
+_add_paths()
# -- Project information -----------------------------------------------------
@@ -39,6 +42,7 @@ release = _get_release()
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
+ 'sphinx.ext.autodoc'
]
# Add any paths that contain templates here, relative to this directory.
@@ -47,7 +51,7 @@ templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.goutput*']
# -- Options for HTML output -------------------------------------------------
diff --git a/docs/index.rst b/docs/index.rst
index d471980..f417c41 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,11 +1,30 @@
Welcome to thcolor's documentation!
===================================
-thcolor is a module for managing color and parsing color textual
-representations.
+This module is a color management module made by `Thomas Touhey`_ (``th``
+is for ``touhey``) for the `textoutpc`_ project, a BBCode to HTML translation
+module. It provides the following features:
+
+- color management and conversions between formats (RGB, HSL, HWB, NCol, …).
+- text-to-color using close-to-CSS format.
+
+To install the module, use pip:
+
+.. code-block:: bash
+
+ $ pip install thcolor
+
+For more information and links, consult `the official website`_.
+
+Sommaire
+--------
.. toctree::
- :maxdepth: 2
- :caption: Contents:
+ :maxdepth: 2
+ angles
+ colors
+.. _Thomas Touhey: https://thomas.touhey.fr/
+.. _textoutpc: https://textout.touhey.pro/
+.. _the official website: https://thcolor.touhey.pro/
diff --git a/setup.cfg b/setup.cfg
index 5096ffd..dfd9203 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,6 +2,8 @@
name = thcolor
version = attr: thcolor.version
url = https://thcolor.touhey.pro/
+project_urls =
+ Documentation = https://thcolor.touhey.pro/docs/
author = Thomas Touhey
author_email = thomas@touhey.fr
description = color management module
diff --git a/thcolor/_angle.py b/thcolor/_angle.py
index c402197..dbcb6f8 100755
--- a/thcolor/_angle.py
+++ b/thcolor/_angle.py
@@ -30,33 +30,89 @@ def _turn(name, value):
# Angle definition.
# ---
-class _AngleType(_Enum):
- """ The angle unit type. """
+class Angle:
+ """ Class representing an angle within thcolor, used for some color
+ representations (most notably hue). Its constructor depends on the
+ first given argument, which represents the angle type as one of the
+ :class:`Angle.Type` constants.
- """ Invalid angle. """
- INVALID = 0
+ .. function:: Angle(Angle.Type.DEG, degrees)
- """ Degrees (360 degrees in a full circle). """
- DEG = 1
+ Create an angle with a value in degrees (canonical values are
+ between 0 and 360 excluded). For example, to create a 270° angle:
- """ Gradiants (400 gradiants in a full circle). """
- GRAD = 2
+ .. code-block:: python
- """ Radiants (2π radiants in a full circle). """
- RAD = 3
+ angle = Angle(Angle.Type.DEG, 270)
- """ Turns (1 turn in a full circle). """
- TURN = 4
+ .. function:: Angle(Angle.Type.GRAD, gradiants)
-class Angle:
- """ Represent an angle with all of its available formats. """
+ Create an angle with a value in gradiants (canonical values are
+ between 0 and 400 excluded). For example, to create a 565.5
+ gradiants angle:
+
+ .. code-block:: python
+
+ angle = Angle(Angle.Type.GRAD, 565.5)
+
+ .. function:: Angle(Angle.Type.RAD, radiants)
+
+ Create an angle with a value in radiants (canonical values are
+ between 0 and 2π excluded). For example, to create a π
+ radiants angle:
+
+ .. code-block:: python
+
+ from math import pi
+ angle = Angle(Angle.Type.RAD, pi)
+
+ .. function:: Angle(Angle.Type.TURN, turns)
+
+ Create an angle with a value in turns (canonical values are
+ between 0 and 1 excluded). For example, to create a 3.5 turns
+ angle:
+
+ .. code-block:: python
+
+ angle = Angle(Angle.Type.TURN, 3.5) """
# Properties to work with:
#
# `_type`: the type as one of the `Angle.Type` constants.
# `_value`: the angle value.
- Type = _AngleType
+ class Type(_Enum):
+ """ Class representing the type of an angle, its unit really.
+ The following types are available:
+
+ .. data:: INVALID
+
+ An invalid angle, for internal processing.
+
+ .. data:: DEG
+
+ An angle in degrees. A full circle is represented by
+ 360 degrees.
+
+ .. data:: GRAD
+
+ An angle in gradiants. A full circle is represented by
+ 400 gradiants.
+
+ .. data:: RAD
+
+ An angle in radiants. A full circle is represented by
+ 2π radiants.
+
+ .. data:: TURN
+
+ An angle in turns. A full circle is represented by 1 turn. """
+
+ INVALID = 0
+ DEG = 1
+ GRAD = 2
+ RAD = 3
+ TURN = 4
def __init__(self, *args, **kwargs):
self._type = Angle.Type.INVALID
@@ -80,12 +136,12 @@ class Angle:
return False
def __repr__(self):
- args = (('type', self._type),)
+ args = (('type', f'{self.__class__.__name__}.{str(self._type)}'),)
if self._type in (Angle.Type.DEG, Angle.Type.GRAD, Angle.Type.RAD,
Angle.Type.TURN):
- args += (('value', self._value),)
+ args += (('value', repr(self._value)),)
- argtext = ', '.join(f'{key}: {repr(value)}' for key, value in args)
+ argtext = ', '.join(f'{key} = {value}' for key, value in args)
return f"{self.__class__.__name__}({argtext})"
# ---
@@ -190,31 +246,36 @@ class Angle:
@property
def type(self):
- """ Get the type. """
+ """ The read-only angle type as one of the :class:`Angle.Type`
+ constants. """
return self._type
@property
def degrees(self):
- """ Get the angle in degrees. """
+ """ The read-only angle value in degrees. If the angle isn't in degrees
+ already, it will be converted automatically. """
return self.turns * 360
@property
def gradiants(self):
- """ Get the angle in gradiants. """
+ """ The read-only angle value in gradiants. If the angle isn't in
+ gradiants already, it will be converted automatically. """
return self.turns * 400
@property
def radiants(self):
- """ Get the angle in radiants. """
+ """ The read-only angle value in radiants. If the angle isn't in
+ radiants already, it will be converted automatically. """
return self.turns * (2 * _pi)
@property
def turns(self):
- """ Get the angle in turns. """
+ """ The read-only angle value in turns. If the angle isn't in
+ turns already, it will be converted automatically. """
if self._type == Angle.Type.DEG:
return self._value / 360
diff --git a/thcolor/_color.py b/thcolor/_color.py
index 55795d2..d273071 100755
--- a/thcolor/_color.py
+++ b/thcolor/_color.py
@@ -103,23 +103,36 @@ def _hue(name, value):
# Color class definition.
# ---
-class _ColorType(_Enum):
- """ The color type. """
+class Color:
+ """ Class representing a color within thcolor. Its constructor depends
+ on the first given argument, which represents the color type as one
+ of the :class:`Color.Type` constants.
- """ Invalid color. """
- INVALID = 0
+ .. function:: Color(Color.Type.RGB, red, green, blue, alpha = 1.0)
- """ RGB/A color. """
- RGB = 1
+ Create a color using its red, green and blue components. Each is
+ expressed as a byte value, from 0 (dark) to 255 (light).
- """ HSL/A color. """
- HSL = 2
+ An alpha value going from 0.0 (invisible) to 1.0 (opaque) can be
+ appended to the base components.
- """ HWB/A color. """
- HWB = 3
+ .. function:: Color(Color.Type.HSL, hue, saturation, lightness, alpha = 1.0)
-class Color:
- """ Represent a color with all of its available formats. """
+ Create a color using its hue, saturation and lightness components.
+ The hue is represented by an :class:`Angle` object, and the
+ saturation and lightness are values going from 0.0 to 1.0.
+
+ An alpha value going from 0.0 (invisible) to 1.0 (opaque) can be
+ appended to the base components.
+
+ .. function:: Color(Color.Type.HWB, hue, whiteness, blackness, alpha = 1.0)
+
+ Create a color using its hue, whiteness and blackness components.
+ The hue is represented by an :class:`Angle` object, and the
+ whiteness and light,ess are values going from 0.0 to 1.0.
+
+ An alpha value going from 0.0 (invisible) to 1.0 (opaque) can be
+ appended to the base components. """
# Properties to work with:
#
@@ -130,26 +143,56 @@ class Color:
# `_sat`, `_lgt`: saturation and light for HSL.
# `_wht`, `_blk`: whiteness and blackness for HWB.
- Type = _ColorType
+ class Type(_Enum):
+ """ Class representing the type of a color, or how it is expressed.
+ The following types are available:
+
+ .. data:: INVALID
+
+ An invalid color, for internal processing.
+
+ .. data:: RGB
+
+ A color expressed through its sRGB components: red, green
+ and blue.
+
+ .. data:: HSL
+
+ A color expressed through its HSL components: hue, saturation
+ and lightness.
+
+ .. data:: HWB
+
+ A color expressed through its HWB components: hue, whiteness
+ and blackness.
+
+ An alpha component can be added to every single one of these
+ types, so it is not included in the type names. """
+
+ INVALID = 0
+ RGB = 1
+ HSL = 2
+ HWB = 3
def __init__(self, *args, **kwargs):
self._type = Color.Type.INVALID
self.set(*args, **kwargs)
def __repr__(self):
- args = (('type', self._type),)
+ args = (('type', f'{self.__class__.__name__}.{str(self._type)}'),)
if self._type == Color.Type.RGB:
- args += (('red', self._r), ('green', self._g), ('blue', self._b))
+ args += (('red', repr(self._r)), ('green', repr(self._g)),
+ ('blue', repr(self._b)))
elif self._type == Color.Type.HSL:
- args += (('hue', self._hue), ('saturation', self._sat),
- ('lightness', self._lgt))
+ args += (('hue', repr(self._hue)), ('saturation', repr(self._sat)),
+ ('lightness', repr(self._lgt)))
elif self._type == Color.Type.HWB:
- args += (('hue', self._hue), ('whiteness', self._wht),
- ('blackness', self._blk))
+ args += (('hue', repr(self._hue)), ('whiteness', repr(self._wht)),
+ ('blackness', repr(self._blk)))
args += (('alpha', self._alpha),)
- argtext = ', '.join(f'{key}: {repr(value)}' for key, value in args)
+ argtext = ', '.join(f'{key} = {value}' for key, value in args)
return f"{self.__class__.__name__}({argtext})"
# ---
@@ -157,7 +200,8 @@ class Color:
# ---
def set(self, *args, **kwargs):
- """ Set the color. """
+ """ Set the color using its constructor arguments and keyword
+ arguments. """
args = list(args)
@@ -260,7 +304,8 @@ class Color:
@property
def type(self):
- """ Get the type. """
+ """ The read-only angle type as one of the :class:`Color.Type`
+ constants. """
return self._type
@@ -392,7 +437,14 @@ class Color:
# ---
def rgb(self):
- """ Get the (red, green, blue) components of the color. """
+ """ Get the sRGB (red, green, blue) components of the color.
+ For example:
+
+ >>> Color.from_text("#876543").rgb()
+ ... (135, 101, 67)
+
+ If the color is not represented as sRGB internally, it will be
+ converted. """
if self._type == Color.Type.RGB:
return (self._r, self._g, self._b)
@@ -404,7 +456,14 @@ class Color:
raise ValueError(f"color type {self._type} doesn't translate to rgb")
def hsl(self):
- """ Get the (hue, saturation, lightness) components of the color. """
+ """ Get the HSL (hue, saturation, lightness) components of the color.
+ For example:
+
+ >>> Color.from_text("hsl(90turn 0% 5%)").hls()
+ ... (Angle(type = Angle.Type.TURN, value = 90.0), 0.05, 0.0)
+
+ If the color is not represented as HSL internally, it will be
+ converted. """
if self._type == Color.Type.RGB:
return _rgb_to_hls(self._r, self._g, self._b)
@@ -416,7 +475,14 @@ class Color:
raise ValueError(f"color type {self._type} doesn't translate to hsl")
def hwb(self):
- """ Get the (hue, whiteness, blackness) components of the color. """
+ """ Get the HWB (hue, whiteness, blackness) components of the color.
+ For example:
+
+ >>> Color.from_text("hwb(.7 turn / 5% 10%)").hwb()
+ ... (Angle(type = Angle.Type.TURN, value = 0.7), 0.05, 0.1)
+
+ If the color is not represented as HSL internally, it will be
+ converted. """
if self._type == Color.Type.RGB:
return _rgb_to_hwb(self._r, self._g, self._b)
@@ -426,7 +492,14 @@ class Color:
return (self._hue, self._wht, self._blk)
def rgba(self):
- """ Get the (red, green, blue, alpha) components of the color. """
+ """ Get the sRGB (red, green, blue) and alpha components of the color.
+ For example:
+
+ >>> Color.from_text("#87654321").rgb()
+ ... (135, 101, 67, 0.1294)
+
+ If the color is not represented as sRGB internally, it will be
+ converted. """
r, g, b = self.rgb()
alpha = self._alpha
@@ -434,7 +507,14 @@ class Color:
return (r, g, b, alpha)
def hsla(self):
- """ Get the (hue, saturation, lightness) components of the color. """
+ """ Get the HSL (hue, saturation, lightness) and alpha components of
+ the color. For example:
+
+ >>> Color.from_text("hsl(90turn 0% 5% .8)").hlsa()
+ ... (Angle(type = Angle.Type.TURN, value = 90.0), 0.05, 0.0, 0.8)
+
+ If the color is not represented as HSL internally, it will be
+ converted. """
h, s, l = self.hsl()
alpha = self._alpha
@@ -442,28 +522,41 @@ class Color:
return (h, s, l, alpha)
def hls(self):
- """ Get the (hue, lightness, saturation) components of the color. """
+ """ Alias for :meth:`hsl` but reverses the lightness and
+ saturation for commodity. """
h, s, l = self.hsl()
return (h, l, s)
def hlsa(self):
- """ Get the (hue, lightness, saturation, alpha) components of
- the color. """
+ """ Alias for :meth:`hsla` but reverses the lightness and
+ saturation for commodity. """
h, s, l, a = self.hsla()
return (h, l, s, a)
def hwba(self):
- """ Get the (hue, whiteness, blackness, alpha) components of
- the color. """
+ """ Get the HWB (hue, whiteness, blackness) and alpha components of
+ the color. For example:
+
+ >>> Color.from_text("hwb(.7 turn / 5% 10% .2)").hwba()
+ ... (Angle(type = Angle.Type.TURN, value = 0.7), 0.05, 0.1, 0.2)
+
+ If the color is not represented as HSL internally, it will be
+ converted. """
h, w, b = self.hwb()
a = self._alpha
return (h, w, b, a)
def css(self):
- """ Get the CSS declarations (with compatibility management). """
+ """ Get the CSS color descriptions, with older CSS specifications
+ compatibility, as a list of strings.
+
+ For example:
+
+ >>> Color(Color.Type.RGB, 18, 52, 86, 0.82).css()
+ ... ["#123456", "rgba(18, 52, 86, 82%)"] """
def _percent(prop):
per = round(prop, 4) * 100
@@ -516,17 +609,26 @@ class Color:
# ---
def from_str(*args, **kwargs):
- """ Alias for `from_text()`. """
+ """ Alias for :meth:`from_text()`. """
return Color.from_text(value)
def from_string(*args, **kwargs):
- """ Alias for `from_text()`. """
+ """ Alias for :meth:`from_text()`. """
return Color.from_text(value)
def from_text(expr, ref = None):
- """ Get a color from a string. """
+ """ Create a color from a string using a :class:`Reference` object.
+ If the ``ref`` argument is ``None``, then the default reference
+ is loaded.
+
+ An example:
+
+ >>> Color.from_text("#123456")
+ ... Color(type = Color.Type.RGB, red = 18, green = 52, blue = 86, alpha = 1.0)
+
+ TODO: describe the text format here. """
if ref is None:
ref = _Reference.default()