diff --git a/INSTALL.rst b/INSTALL.rst new file mode 100644 index 0000000..4ae92b7 --- /dev/null +++ b/INSTALL.rst @@ -0,0 +1,30 @@ +========================= +Installation instructions +========================= + +colortools can be installed using pip:: + + $ python -m pip install colortools + +This command will fetch the archive and its dependencies from the internet and +install them. You may need to pick either ``python2``` or ``python3`` in +order to select the correct python version. + +If you've downloaded the tarball, unpack it, and execute:: + + $ python setup.py install + +In either case, it is possible to perform local user installs by appending the +``--user`` option. + + +Troubleshoot +------------ + +Windows users may find that these command will only works if typed from Python's +installation directory. + +Some Linux distributions (e.g. Ubuntu) install Python without installing pip. +Please install it before. If you don't have root privileges, download the +get-pip.py script at https://bootstrap.pypa.io/get-pip.py and execute it as +``python get-pip.py --user``. \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d1f819f --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +colortools. +Copyright (C) Fábio Macêdo Mendes + +This program 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 3 of the License, or (at your option) any later +version. + +This program 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 +this program. If not, see . \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..38e2064 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,6 @@ +include INSTALL.rst +include README.rst +include LICENSE +include VERSION +include requirements.txt +include tasks.py \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..c4d316b --- /dev/null +++ b/README.rst @@ -0,0 +1,48 @@ +The :mod:`colortools` defines a :cls:`Color` object that can be used to +represent and manipulate colors in various contexts. It was created with the +:mod:`FGAme` game engine, but it can also be used in other contexts such as +arts and web applications. + +Colors can be initialized from the RGB/RGBA components, in which each component +is in the 0-255 range. + +>>> w1 = Color(255, 255, 255) +>>> w2 = Color(255, 255, 255, 255) +>>> w1 == w2 +True + +The constructor also accepts color names, hex strings or hex numbers. + +>>> Color('white') == Color('#FFF') == Color(0xffffff) == Color(255, 255, 255) +True + +All names in CSS3.0 are accepted (see the complete list `here`). +It is worth noting that although the *G* in RGB stands for green, the color +with a full green value is called *lime*, while "green" is **#008000** rather than +**#00FF00**. + +Usage and API +------------- + +Color objects are immutable and behave like a tuple of 4 values. + +>>> list(Color('green')) +[0, 80, 0, 255] + +It also exposes several different representations of a color using the +appropriate attribute. + +>>> blue = Color('blue') +>>> blue.rbg +(255, 0, 0) + +>>> blue.rgbf +(1.0, 0.0, 0.0) + +>>> blue.rgbu +16711680 + +Color objects also implement a few useful transformations + +>>> blue.darken(0.1) # darken by 10% +Color(229, 0, 0) \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6da28dd --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.1 \ No newline at end of file diff --git a/boilerplate.ini b/boilerplate.ini new file mode 100644 index 0000000..adf9f13 --- /dev/null +++ b/boilerplate.ini @@ -0,0 +1,54 @@ +[options] +project = colortools +author = Fábio Macêdo Mendes +email = fabiomacedomendes@gmail.com +version = 0.1.0 +license = gpl +pyname = colortools + +[file hashes] +VERSION = 5dGLTiwdwZdXde44WrQchQ== + +.gitignore = iu2Zn1vki51Un7bnWFivjQ== + +README.rst = uPdLHOwxE8pcjE53T+O1iQ== + +INSTALL.rst = k/F9ZlbJ3CYk9U2SVpTXNw== + +LICENSE = HlS8/vbcwNyIFgy6FnicyQ== + +tasks.py = 6GOUg08jHdzGhTHggdHrsA== + +setup.py = jO1VBZHTTbhmUaSvZLAF7Q== + +MANIFEST.in = BR29dThJGj3LpFi457HDsw== + +requirements.txt = 9/H6jW0guTB5y4aYQ+29FA== + +requirements-dev.txt = IrFEvOtt6TH8c3X1z/Qgrg== + +src/colortools/__init__.py = 1zLEKfdEAfCqC71HMe3ifA== + +src/colortools/__meta__.py = ITV3jN2HNctrB2gBnc7WNA== + +src/colortools/__main__.py = lmKoqR1olD5mZ7TNIYbAug== + +src/colortools/tests/test_colortools.py = vb8s3uOxr/85JLBtXNlJ6Q== + +docs/conf.py = /k5SqU8PfyrF6IJZu6cPfg== + +docs/index.rst = BcUr6MxiaEfZvfinE/Qx7g== + +docs/install.rst = uEvES6MgO5srBNLXAVqL/Q== + +docs/license.rst = T7JCt0IDBksEuohsOM3rxQ== + +docs/apidoc.rst = id1zCZJLYOZFBT5/kbmjBw== + +docs/warning.rst = O2SiWXTOqJBpkFGu1aW+VA== + +docs/make.bat = AyOyYMZZh/t1xRThRsgGOA== + +docs/Makefile = sHJpgsLZvsiYQotqGNXIhw== + + diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..ee10e24 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/colortools.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/colortools.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/colortools" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/colortools" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." \ No newline at end of file diff --git a/docs/apidoc.rst b/docs/apidoc.rst new file mode 100644 index 0000000..df069bc --- /dev/null +++ b/docs/apidoc.rst @@ -0,0 +1,8 @@ +============= +API Reference +============= + +API documentation for the colortools module. + +.. automodule:: colortools + :members: \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..2ce7594 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# boilerplate documentation build configuration file, created by +# sphinx-quickstart on Thu Mar 24 05:34:39 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'colortools' +project_title = project.title() +author = 'Fábio Macêdo Mendes' +copyright = '2016, %s' % author + + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +dirname = os.path.dirname(__file__) +projdir = os.path.dirname(dirname) +release = open(os.path.join(projdir, 'VERSION')).read().strip() +version = '.'.join(release.split('.')[:2]) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = '%sdoc' % project + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, '%s.tex' % project, '%s Documentation' % project, + author, 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, project, '%s Documentation' % project, + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, project, '%s Documentation' % project, + author, project, 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..3af341b --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +================================================================================ +Welcome to colortools's documentation! +================================================================================ + +.. include:: warning.rst + +.. include:: ../README.rst + +.. toctree:: + :maxdepth: 2 + + Installation instructions + API documentation + License + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 0000000..b914117 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1 @@ +.. include:: ../INSTALL.rst \ No newline at end of file diff --git a/docs/license.rst b/docs/license.rst new file mode 100644 index 0000000..4836374 --- /dev/null +++ b/docs/license.rst @@ -0,0 +1,5 @@ +======= +License +======= + +.. include:: ../LICENSE \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..84ad9ce --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\colortools.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\colortools.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end \ No newline at end of file diff --git a/docs/warning.rst b/docs/warning.rst new file mode 100644 index 0000000..f3bf051 --- /dev/null +++ b/docs/warning.rst @@ -0,0 +1,9 @@ +.. warning:: Beta software + You are using a software that has not reached a stable version yet. Please + beware that interfaces might change, APIs might disappear and general + breakage can occur before *1.0*. + + If you plan to use this software for something important, please read the + roadmap, and the issue tracker in Github. If you are unsure about the + future of this project, please talk to the developers, or (better yet) get + involved with the development of colortools! \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..6d33e60 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +-e . +pytest +boilerplate +mock +invoke \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ecf975e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +-e . \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..dc81e76 --- /dev/null +++ b/setup.py @@ -0,0 +1,60 @@ +# -*- coding: utf8 -*- +# +# This file were created by Python Boilerplate. Use boilerplate to start simple +# usable and best-practices compliant Python projects. +# +# Learn more about it at: http://github.com/fabiommendes/boilerplate/ +# + +import os +from setuptools import setup, find_packages + + +# Meta information +name = 'colortools' +project = 'colortools' +author = 'Fábio Macêdo Mendes' +version = open('VERSION').read().strip() +dirname = os.path.dirname(__file__) + +# Save version and author to __meta__.py +file_name = os.path.join(dirname, 'src', project, '__meta__.py') +with open(file_name, 'w', encoding='utf-8') as F: + F.write('__version__ = %r\n' + '__author__ = %r\n' % (version, author)) + +setup( + # Basic info + name=name, + version=version, + author=author, + author_email='fabiomacedomendes@gmail.com', + url='', + description='A short description for your project.', + long_description=open('README.rst').read(), + + # Classifiers (see https://pypi.python.org/pypi?%3Aaction=list_classifiers) + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU General Public License (GPL)', + 'Operating System :: POSIX', + 'Programming Language :: Python', + 'Topic :: Software Development :: Libraries', + ], + + # Packages and depencies + package_dir={'': 'src'}, + packages=find_packages('src'), + install_requires=[], + extras_require={ + 'dev': [ + 'pytest', + 'python-boilerplate', + ], + }, + + # Other configurations + zip_safe=False, + platforms='any', +) \ No newline at end of file diff --git a/src/colortools/__init__.py b/src/colortools/__init__.py new file mode 100644 index 0000000..e18f8c8 --- /dev/null +++ b/src/colortools/__init__.py @@ -0,0 +1,2 @@ +from .__meta__ import __author__, __version__ +from .base import * \ No newline at end of file diff --git a/src/colortools/__meta__.py b/src/colortools/__meta__.py new file mode 100644 index 0000000..27b7bea --- /dev/null +++ b/src/colortools/__meta__.py @@ -0,0 +1,2 @@ +__version__ = '0.1.1' +__author__ = 'Fábio Macêdo Mendes' diff --git a/src/colortools/base.py b/src/colortools/base.py new file mode 100644 index 0000000..2c8da22 --- /dev/null +++ b/src/colortools/base.py @@ -0,0 +1,551 @@ +import random +from functools import lru_cache +from math import sqrt, acos + +__all__ = ['Color', 'colorproperty', 'rgb', 'rgba', 'COLOR_NAMES'] + + +class Color(object): + """ + Represents a color. + """ + __slots__ = ['_red', '_green', '_blue', '_alpha'] + _CACHE = {} + _RANGE = (0, 1, 2, 3) + + @classmethod + def from_rgba(cls, red=0, green=0, blue=0, alpha=255): + """ + Create color from RGBa components. + """ + + new = object.__new__(cls) + new._fromrgba(red, green, blue, alpha) + return new + + def __init__(self, *args, **kwargs): + # RGB parameters (with or without alpha) + if len(args) in [3, 4]: + self._fromrgba(*args, **kwargs) + + elif len(args) == 1: + value, = args + + # Start from another color, tuple or list of values + if isinstance(value, (list, tuple, Color)): + self._fromrgba(*value) + + # Start from string (either hex or color name) + elif isinstance(value, str): + if value.startswith('#'): + self._fromhex(value, **kwargs) + elif value == 'random': + self._fromrgba(*[random.randint(0, 255) for _ in range(3)]) + else: + self._fromname(value, **kwargs) + + # Start from integer + elif isinstance(value, int): + r = (value & 0xff0000) >> 16 + g = (value & 0xff00) >> 8 + b = (value & 0xff) >> 0 + self._fromrgba(r, g, b, **kwargs) + + elif len(args) == 0: + raise NotImplemented + else: + TypeError('invalid number of arguments: %s' % len(args)) + + def _fromrgba(self, r=0, g=0, b=0, a=255): + self._red = int(r) + self._green = int(g) + self._blue = int(b) + self._alpha = int(a) + + def _fromhex(self, value, alpha=255): + value = value[1:] + if len(value) in [3, 4]: + value = ''.join(c + c for c in value) + if len(value) == 6: + a = alpha + r, g, b = ( + int(value[2 * i:2 * i + 2], 16) for i in range(3)) + elif len(value) == 8: + r, g, b, a = ( + int(value[2 * i:2 * i + 2], 16) for i in range(4)) + else: + raise ValueError('invalid hex code: #%s' % value) + self._fromrgba(r, g, b, a) + + def _fromname(self, value): + value = value.lower() + self._fromrgba(*self._CACHE[value].rgba) + + @property + def red(self): + """ + Red component (from 0 to 255) + """ + return self._red + + @property + def green(self): + """ + Green component (from 0 to 255) + """ + + return self._green + + @property + def blue(self): + """ + Blue component (from 0 to 255) + """ + + return self._blue + + @property + def alpha(self): + """ + Alpha component (0: transparent, 255: solid color) + """ + + return self._alpha + + @property + def hue(self): + """ + Hue component (from 0 to 255). + """ + + return NotImplemented + + @property + def saturation(self): + """ + Saturation component (from 0 to 255). + """ + + return NotImplemented + + @property + def intensity(self): + """ + Intensity component (from 0 to 255). + """ + + return NotImplemented + + @property + def rgba(self): + """ + Tuple with (red, green, blue, alpha) + """ + return tuple(self) + + @property + def rgb(self): + """ + Tuple with (red, green, blue) + """ + return self[:3] + + @property + def rgbf(self): + """ + Floating-point version of rgb, normalized in the [0, 1] interval. + """ + + return tuple(x / 255. for x in self[:3]) + + @property + def rgbaf(self): + """ + Floating-point version of rgba, normalized in the [0, 1] interval. + """ + + return tuple(x / 255. for x in self) + + @property + def rgbau(self): + """ + RGBA as a single 32 bit unsigned integer. + """ + + c = self + return (c[0] << 24) + (c[1] << 16) + (c[2] << 8) + c[3] + + @property + def rgbu(self): + c = self + return (c[0] << 16) + (c[1] << 8) + c[2] + + @property + def hsi(self): + return self.hsia[:-1] + + @property + def hsia(self): + # (H)ue, (S)aturation, (I)ntensity + # ref: https://en.wikipedia.org/wiki/RGB_color_model#Nonlinearity + R, G, B, a = self + + I = (R + G + B) / 3 + S = 1 - min(R, G, B) / I + + # Normalized + r, g, b = R / 255, G / 255, B / 255 + h_numer = acos(((r - g) + (r - b)) / 2) + h_denom = sqrt((r - b) ** 2 + (r - b) * (g - b)) + return h_numer / h_denom, S, I, a + + @property + def hsiaf(self): + return tuple(x / 255 for x in self.hsia) + + @property + def hsif(self): + return tuple(x / 255 for x in self.hsi) + + def copy(self, red=None, green=None, blue=None, alpha=None, **kwds): + """Copia a cor possivelmente trocando o valor de alguma das componentes + RGBA ou componentes HSL ou HSI. + + Examples + -------- + + >>> color = Color('white') + >>> color.copy(red=80, alpha=128) + Color(80, 255, 255, 128) + """ + R, G, B, A = self + + if red is not None: + R = red + if green is not None: + G = green + if blue is not None: + B = blue + if alpha is not None: + A = alpha + if kwds: + raise NotImplementedError + + return Color(R, G, B, A) + + # Métodos mágicos + def __repr__(self): + return 'Color%s' % (tuple(self),) + + def __len__(self): + return 4 + + def __eq__(self, other): + try: + return ( + (self._red == other._red) and + (self._green == other._green) and + (self._blue == other._blue) and + (self._alpha == other._alpha)) + except (AttributeError, TypeError): + if len(other) == 4: + return all(x == y for (x, y) in zip(self, other)) + elif len(other) == 3: + return ((self._alpha == 255) and + all(x == y for (x, y) in zip(self, other))) + return False + + def __iter__(self): + yield self._red + yield self._green + yield self._blue + yield self._alpha + + def __getitem__(self, key): + if isinstance(key, int): + if key == 0: + return self._red + elif key == 1: + return self._green + elif key == 2: + return self._blue + elif key == 3: + return self._alpha + elif key < 0: + key = len(self) - key + if key < 0: + raise IndexError + return self[key] + else: + return tuple(self[i] for i in self._RANGE[key]) + raise IndexError(key) + + def __hash__(self): + return hash(self.u_rgba) + + +Color._CACHE.update( + # HTML 4.01 colors. + # See https://en.wikipedia.org/wiki/Web_colors + white=Color('#ffffff'), + silver=Color('#c0c0c0'), + gray=Color('#808080'), + black=Color('#000000'), + red=Color('#ff0000'), + maroon=Color('#800000'), + yellow=Color('#ffff00'), + olive=Color('#808000'), + lime=Color('#00ff00'), + green=Color('#008000'), + aqua=Color('#00ffff'), + teal=Color('#008080'), + blue=Color('#0000ff'), + navy=Color('#000080'), + fuschia=Color('#ff00ff'), + purple=Color('#800080'), + transparent=Color('#ffffff00'), + null=Color('#00000000'), +) + +# SVG extended Colors +# http://www.w3.org/TR/css3-color/#svg-color +COLOR_NAMES = [ + ['aliceblue', '#F0F8FF'], + ['antiquewhite', '#FAEBD7'], + ['aqua', '#00FFFF'], + ['aquamarine', '#7FFFD4'], + ['azure', '#F0FFFF'], + ['beige', '#F5F5DC'], + ['bisque', '#FFE4C4'], + ['black', '#000000'], + ['blanchedalmond', '#FFEBCD'], + ['blue', '#0000FF'], + ['blueviolet', '#8A2BE2'], + ['brown', '#A52A2A'], + ['burlywood', '#DEB887'], + ['cadetblue', '#5F9EA0'], + ['chartreuse', '#7FFF00'], + ['chocolate', '#D2691E'], + ['coral', '#FF7F50'], + ['cornflowerblue', '#6495ED'], + ['cornsilk', '#FFF8DC'], + ['crimson', '#DC143C'], + ['cyan', '#00FFFF'], + ['darkblue', '#00008B'], + ['darkcyan', '#008B8B'], + ['darkgoldenrod', '#B8860B'], + ['darkgray', '#A9A9A9'], + ['darkgreen', '#006400'], + ['darkgrey', '#A9A9A9'], + ['darkkhaki', '#BDB76B'], + ['darkmagenta', '#8B008B'], + ['darkolivegreen', '#556B2F'], + ['darkorange', '#FF8C00'], + ['darkorchid', '#9932CC'], + ['darkred', '#8B0000'], + ['darksalmon', '#E9967A'], + ['darkseagreen', '#8FBC8F'], + ['darkslateblue', '#483D8B'], + ['darkslategray', '#2F4F4F'], + ['darkslategrey', '#2F4F4F'], + ['darkturquoise', '#00CED1'], + ['darkviolet', '#9400D3'], + ['deeppink', '#FF1493'], + ['deepskyblue', '#00BFFF'], + ['dimgray', '#696969'], + ['dimgrey', '#696969'], + ['dodgerblue', '#1E90FF'], + ['firebrick', '#B22222'], + ['floralwhite', '#FFFAF0'], + ['forestgreen', '#228B22'], + ['fuchsia', '#FF00FF'], + ['gainsboro', '#DCDCDC'], + ['ghostwhite', '#F8F8FF'], + ['gold', '#FFD700'], + ['goldenrod', '#DAA520'], + ['gray', '#808080'], + ['green', '#008000'], + ['greenyellow', '#ADFF2F'], + ['grey', '#808080'], + ['honeydew', '#F0FFF0'], + ['hotpink', '#FF69B4'], + ['indianred', '#CD5C5C'], + ['indigo', '#4B0082'], + ['ivory', '#FFFFF0'], + ['khaki', '#F0E68C'], + ['lavender', '#E6E6FA'], + ['lavenderblush', '#FFF0F5'], + ['lawngreen', '#7CFC00'], + ['lemonchiffon', '#FFFACD'], + ['lightblue', '#ADD8E6'], + ['lightcoral', '#F08080'], + ['lightcyan', '#E0FFFF'], + ['lightgoldenrodyellow', '#FAFAD2'], + ['lightgray', '#D3D3D3'], + ['lightgreen', '#90EE90'], + ['lightgrey', '#D3D3D3'], + ['lightpink', '#FFB6C1'], + ['lightsalmon', '#FFA07A'], + ['lightseagreen', '#20B2AA'], + ['lightskyblue', '#87CEFA'], + ['lightslategray', '#778899'], + ['lightslategrey', '#778899'], + ['lightsteelblue', '#B0C4DE'], + ['lightyellow', '#FFFFE0'], + ['lime', '#00FF00'], + ['limegreen', '#32CD32'], + ['linen', '#FAF0E6'], + ['magenta', '#FF00FF'], + ['maroon', '#800000'], + ['mediumaquamarine', '#66CDAA'], + ['mediumblue', '#0000CD'], + ['mediumorchid', '#BA55D3'], + ['mediumpurple', '#9370DB'], + ['mediumseagreen', '#3CB371'], + ['mediumslateblue', '#7B68EE'], + ['mediumspringgreen', '#00FA9A'], + ['mediumturquoise', '#48D1CC'], + ['mediumvioletred', '#C71585'], + ['midnightblue', '#191970'], + ['mintcream', '#F5FFFA'], + ['mistyrose', '#FFE4E1'], + ['moccasin', '#FFE4B5'], + ['navajowhite', '#FFDEAD'], + ['navy', '#000080'], + ['oldlace', '#FDF5E6'], + ['olive', '#808000'], + ['olivedrab', '#6B8E23'], + ['orange', '#FFA500'], + ['orangered', '#FF4500'], + ['orchid', '#DA70D6'], + ['palegoldenrod', '#EEE8AA'], + ['palegreen', '#98FB98'], + ['paleturquoise', '#AFEEEE'], + ['palevioletred', '#DB7093'], + ['papayawhip', '#FFEFD5'], + ['peachpuff', '#FFDAB9'], + ['peru', '#CD853F'], + ['pink', '#FFC0CB'], + ['plum', '#DDA0DD'], + ['powderblue', '#B0E0E6'], + ['purple', '#800080'], + ['red', '#FF0000'], + ['rosybrown', '#BC8F8F'], + ['royalblue', '#4169E1'], + ['saddlebrown', '#8B4513'], + ['salmon', '#FA8072'], + ['sandybrown', '#F4A460'], + ['seagreen', '#2E8B57'], + ['seashell', '#FFF5EE'], + ['sienna', '#A0522D'], + ['silver', '#C0C0C0'], + ['skyblue', '#87CEEB'], + ['slateblue', '#6A5ACD'], + ['slategray', '#708090'], + ['slategrey', '#708090'], + ['snow', '#FFFAFA'], + ['springgreen', '#00FF7F'], + ['steelblue', '#4682B4'], + ['tan', '#D2B48C'], + ['teal', '#008080'], + ['thistle', '#D8BFD8'], + ['tomato', '#FF6347'], + ['turquoise', '#40E0D0'], + ['violet', '#EE82EE'], + ['wheat', '#F5DEB3'], + ['white', '#FFFFFF'], + ['whitesmoke', '#F5F5F5'], + ['yellow', '#FFFF00'], + ['yellowgreen', '#9ACD32'] +] +for _name, _code in COLOR_NAMES: + Color._CACHE.setdefault(_name, Color(_code)) + + +class colorproperty(property): + """ + Automatically converts attribute assignments to color objects. + """ + + def __init__(self, name, default=None): + default = (None if default is None else Color(default)) + attr = '_' + name + + def fget(self): + return getattr(self, attr, default) + + def fset(self, value): + if value is None: + setattr(self, attr, default) + elif isinstance(value, Color): + setattr(self, attr, value) + else: + setattr(self, attr, Color(value)) + + def fdel(self): + if hasattr(self, attr): + delattr(self, attr) + else: + raise AttributeError(name) + + super(colorproperty, self).__init__(fget, fset, fdel) + + +def color(*args): + """ + Faster constructor of Color() objects. + + Accept all positional arguments as Color(), but rejects any keyword + arguments. + + It avoids a few expansive checks in the Color() constructor and also caches + the results. + """ + + if len(args) == 1: + value = args[0] + if isinstance(value, Color): + return value + elif isinstance(value, str): + if not value.startswith('#'): + return _cached_hex(value) + + return _cached_color(args) + + +@lru_cache(maxsize=512) +def _cached_hex(value): + return Color(value) + + +@lru_cache(maxsize=512) +def _cached_color(args): + return Color(*args) + + +def rgb(color): + """ + Convert input in a tuple of (red, green, blue) colors. + + Null values are converted to solid black. + """ + + try: + return color.rgb + except AttributeError: + return Color(color or 'black').rgb + + +def rgba(color): + """ + Convert input in a tuple of (red, green, blue, alpha) components + + Null values are converted to solid black. + """ + + try: + return color.rgba + except AttributeError: + return Color(color or 'black').rgba + +import pgzero \ No newline at end of file diff --git a/src/colortools/tests/test_colortools.py b/src/colortools/tests/test_colortools.py new file mode 100644 index 0000000..7d53c6b --- /dev/null +++ b/src/colortools/tests/test_colortools.py @@ -0,0 +1,47 @@ +import pytest +from colortools import Color + + +def test_init_rgb_color_correctly(): + c = Color(1, 2, 3) + assert c.red == 1 + assert c.green == 2 + assert c.blue == 3 + assert c.alpha == 255 + + +def test_init_from_rgba(): + assert Color(1, 2, 3, 255) == Color(1, 2, 3) + + +def test_init_color_from_name(): + assert Color('red') == Color(255, 0, 0) + assert Color('lime') == Color(0, 255, 0) # HTML name convention... + assert Color('blue') == Color(0, 0, 255) + + +def test_init_from_3_hex(): + assert Color('#0F0') == Color(0, 255, 0) + + +def test_init_from_4_hex(): + assert Color('#0F01') == Color(0, 255, 0, 17) + + +def test_init_from_6_hex(): + assert Color('#00FF00') == Color(0, 255, 0) + + +def test_init_from_8_hex(): + assert Color('#00FF0080') == Color(0, 255, 0, 128) + + +def test_color_compares_with_tuple(): + color = Color('red') + assert color == (255, 0, 0, 255) + assert color == (255, 0, 0) + + +def test_init_color_from_color(): + black = Color('black') + assert Color(black) == black