diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 7396be3..b63e309 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at info@4r7.ir. All +reported by contacting the project team at info@samila.site. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. diff --git a/.github/workflows/publish_conda.yaml b/.github/workflows/publish_conda.yaml new file mode 100644 index 0000000..97751a8 --- /dev/null +++ b/.github/workflows/publish_conda.yaml @@ -0,0 +1,18 @@ +name: publish_conda + +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - '*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: publish-to-conda + uses: sepandhaghighi/conda-package-publish-action@v1.2 + with: + subDir: 'otherfiles' + AnacondaToken: ${{ secrets.ANACONDA_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish_pypi.yml similarity index 100% rename from .github/workflows/publish.yml rename to .github/workflows/publish_pypi.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25b00ac..19c4376 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,3 +60,6 @@ jobs: run: | codecov if: matrix.python-version == 3.7 && matrix.os == 'ubuntu-latest' + - name: cProfile + run: | + python -m cProfile -s cumtime otherfiles/samila_profile.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b399f..eeb16a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.9] - 2022-09-28 +### Added +- Anaconda workflow +### Changed +- `README.md` updated +- `CODE_OF_CONDUCT.md` updated +- `demo.ipynb` updated +- `cmap` parameter added to `plot` method +- Random mode modified +- Test system modified +- `generate` method optimized +- `samila_help` function updated +- `load_data` and `load_config` functions error handling updated ## [0.8] - 2022-06-01 ### Added - `INVALID_COLOR_TYPE_ERROR` error @@ -123,7 +136,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - `generate` method - `nft_storage` method -[Unreleased]: https://github.com/sepandhaghighi/samila/compare/v0.8...dev +[Unreleased]: https://github.com/sepandhaghighi/samila/compare/v0.9...dev +[0.9]: https://github.com/sepandhaghighi/samila/compare/v0.8...v0.9 [0.8]: https://github.com/sepandhaghighi/samila/compare/v0.7...v0.8 [0.7]: https://github.com/sepandhaghighi/samila/compare/v0.6...v0.7 [0.6]: https://github.com/sepandhaghighi/samila/compare/v0.5...v0.6 diff --git a/README.md b/README.md index 0febb45..1ca9b22 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ PyPI version + Samila-Colab @@ -88,7 +89,7 @@ Samila is a generative art generator written in Python, Samila let's you create ### Source code -- Download [Version 0.8](https://github.com/sepandhaghighi/samila/archive/v0.8.zip) or [Latest Source ](https://github.com/sepandhaghighi/samila/archive/dev.zip) +- Download [Version 0.9](https://github.com/sepandhaghighi/samila/archive/v0.9.zip) or [Latest Source ](https://github.com/sepandhaghighi/samila/archive/dev.zip) - Run `pip install -r requirements.txt` or `pip3 install -r requirements.txt` (Need root access) - Run `python3 setup.py install` or `python setup.py install` (Need root access) @@ -96,12 +97,17 @@ Samila is a generative art generator written in Python, Samila let's you create - Check [Python Packaging User Guide](https://packaging.python.org/installing/) -- Run `pip install samila==0.8` or `pip3 install samila==0.8` (Need root access) +- Run `pip install samila==0.9` or `pip3 install samila==0.9` (Need root access) ### Easy install - Run `easy_install --upgrade samila` (Need root access) +### Conda + +- Check [Conda Managing Package](https://conda.io) +- `conda install -c sepandhaghighi samila` (Need root access) + ## Usage @@ -181,9 +187,34 @@ Samila is a generative art generator written in Python, Samila let's you create 4. Random (example: `color="random"`) 5. Complement (example: `color="complement", bgcolor="blue"`) 6. Transparent (example: `bgcolor="transparent"`) + 7. List (example: `color=["black", "#fffeef",...]`) ⚠️ **Transparent** mode is only available for background +⚠️ **List** mode is only available for color + +⚠️ In **List** mode, the length of this list must be equal to the lengths of data1 and data2 + +#### Point Color +You can make your custom color map and use it in Samila + +```pycon +>>> colorarray = [ +... [0.7, 0.2, 0.2, 1], +... [0.6, 0.3, 0.2, 1], +... "black", +... [0.4, 0.4, 0.3, 1], +... [0.3, 0.4, 0.4, 1], +... "#ff2561"] +>>> g.generate() +>>> g.seed +454893 +>>> g.plot(cmap=colorarray, color=g.data2, projection=Projection.POLAR) +>>> plt.show() +``` + + + ### Regeneration ```pycon >>> g = GenerativeImage(f1, f2) @@ -333,7 +364,7 @@ Samila can be used online in interactive Jupyter Notebooks via the Binder or Col ## Issues & bug reports -Just fill an issue and describe it. We'll check it ASAP! +Just fill an issue and describe it. We'll check it ASAP! or send an email to [info@samila.site](mailto:info@samila.site "info@samila.site"). - Please complete the issue template diff --git a/autopep8.bat b/autopep8.bat index 24dc840..7029efb 100644 --- a/autopep8.bat +++ b/autopep8.bat @@ -1,2 +1,2 @@ -python -m autopep8 samila --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose +python -m autopep8 samila --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose --ignore=E721 python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose diff --git a/autopep8.sh b/autopep8.sh index 9b77a9c..5b2f3c6 100644 --- a/autopep8.sh +++ b/autopep8.sh @@ -1,3 +1,3 @@ #!/bin/sh -python -m autopep8 samila --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose +python -m autopep8 samila --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose --ignore=E721 python -m autopep8 setup.py --recursive --aggressive --aggressive --in-place --pep8-passes 2000 --verbose diff --git a/dev-requirements.txt b/dev-requirements.txt index a4fe8ed..4f8ffee 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,5 @@ -matplotlib==3.5.2 -art==5.6 +matplotlib==3.5.3 +art==5.7 vulture>=1.0 bandit>=1.5.1 pydocstyle>=3.0.0 diff --git a/examples/demo.ipynb b/examples/demo.ipynb index 8e32f40..8f0c690 100644 --- a/examples/demo.ipynb +++ b/examples/demo.ipynb @@ -134,9 +134,7 @@ "source": [ "## Color\n", "\n", - "We can assign colors for both the background as well as the line\n", - "\n", - "Supported colors are available in `VALID_COLORS` list\n" + "We can assign colors for both the background as well as the line\n" ] }, { @@ -155,6 +153,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "* Supported colors are available in `VALID_COLORS` list\n", "* `color` and `bgcolor` parameters supported formats:\n", "\n", " 1. Color name (example: `color=\"yellow\"`)\n", @@ -163,8 +162,40 @@ " 4. Random (example: `color=\"random\"`)\n", " 5. Complement (example: `color=\"complement\", bgcolor=\"blue\"`)\n", " 6. Transparent (example: `bgcolor=\"transparent\"`)\n", + " 7. List (example: `color=[\"black\", \"#fffeef\",...]`)\n", "\n", - "⚠️ **Transparent** mode is only available for background" + "⚠️ **Transparent** mode is only available for background\n", + "\n", + "⚠️ **List** mode is only available for color\n", + "\n", + "⚠️ In **List** mode, the length of this list must be equal to the lengths of data1 and data2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Point Color\n", + "\n", + "You can make your custom color map and use it in Samila" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "colorarray = [\n", + " [0.7, 0.2, 0.2, 1],\n", + " [0.6, 0.3, 0.2, 1],\n", + " \"black\",\n", + " [0.4, 0.4, 0.3, 1],\n", + " [0.3, 0.4, 0.4, 1],\n", + " \"#ff2561\"]\n", + "g2.generate()\n", + "g2.plot(cmap=colorarray, color=g2.data2, projection=Projection.POLAR)\n", + "plt.show()" ] }, { @@ -351,8 +382,31 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Samila Demo", + "title_sidebar": "Samila Demo", + "toc_cell": false, + "toc_position": { + "height": "382px", + "left": "10px", + "top": "10px", + "width": "256px" + }, + "toc_section_display": false, + "toc_window_display": true + }, + "vscode": { + "interpreter": { + "hash": "e7370f93d1d0cde622a1f8e1c04877d8463912d04d973331ad4851f04de6915a" + } } }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/otherfiles/images/8.png b/otherfiles/images/8.png new file mode 100644 index 0000000..3c74ab8 Binary files /dev/null and b/otherfiles/images/8.png differ diff --git a/otherfiles/meta.yaml b/otherfiles/meta.yaml new file mode 100644 index 0000000..7d9c5d1 --- /dev/null +++ b/otherfiles/meta.yaml @@ -0,0 +1,37 @@ +{% set name = "samila" %} +{% set version = "0.9" %} + +package: + name: {{ name|lower }} + version: {{ version }} +source: + git_url: https://github.com/sepandhaghighi/samila + git_rev: v{{ version }} +build: + noarch: python + number: 0 + script: {{ PYTHON }} -m pip install . -vv +requirements: + host: + - pip + - setuptools + - python >=3.5 + run: + - art >=1.8 + - matplotlib >=3.0.0 + - requests >=2.20.0 + - python >=3.5 +about: + home: https://github.com/sepandhaghighi/samila + license: MIT + license_family: MIT + summary: Generative Art Generator + description: | + Samila is a generative art generator written in Python, Samila let's you create arts based on many thousand points. The position of every single point is calculated by a formula, which has random parameters. Because of the random numbers, every image looks different. + + Website: https://www.samila.site + + Repo: https://github.com/sepandhaghighi/samila +extra: + recipe-maintainers: + - sepandhaghighi diff --git a/otherfiles/samila_profile.py b/otherfiles/samila_profile.py new file mode 100644 index 0000000..18a9deb --- /dev/null +++ b/otherfiles/samila_profile.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +"""Samila profile.""" +from samila import * +import random +import math + + +def f1(x, y): + result = random.uniform(-1, 1) * x**2 - math.sin(y**2) + abs(y-x) + return result + + +def f2(x, y): + result = random.uniform(-1, 1) * y**3 - math.cos(x**2) + 2*x + return result + +g = GenerativeImage(f1, f2) +g.generate(seed=200) +g.plot() +g.save_image("test.png") diff --git a/otherfiles/version_check.py b/otherfiles/version_check.py index 66ca7a4..91ae0ee 100644 --- a/otherfiles/version_check.py +++ b/otherfiles/version_check.py @@ -4,7 +4,7 @@ import os import sys import codecs Failed = 0 -SAMILA_VERSION = "0.8" +SAMILA_VERSION = "0.9" SETUP_ITEMS = [ @@ -19,9 +19,12 @@ CHANGELOG_ITEMS = [ "https://github.com/sepandhaghighi/samila/compare/v{0}...dev", "[{0}]:"] PARAMS_ITEMS = ['SAMILA_VERSION = "{0}"'] + +META_ITEMS = ['% set version = "{0}" %'] + FILES = { "setup.py": SETUP_ITEMS, "README.md": README_ITEMS, "CHANGELOG.md": CHANGELOG_ITEMS, os.path.join( - "samila", "params.py"): PARAMS_ITEMS} + "samila", "params.py"): PARAMS_ITEMS, os.path.join("otherfiles", "meta.yaml"): META_ITEMS} TEST_NUMBER = len(FILES.keys()) diff --git a/samila/functions.py b/samila/functions.py index 139e82d..dcd593e 100644 --- a/samila/functions.py +++ b/samila/functions.py @@ -8,12 +8,15 @@ import re import json import random import matplotlib +from matplotlib import cm +from matplotlib.colors import ListedColormap from .params import DEFAULT_START, DEFAULT_STOP, DEFAULT_STEP, DEFAULT_COLOR, DEFAULT_IMAGE_SIZE, DEFAULT_DEPTH +from .params import DEFAULT_CMAP, DEFAULT_CMAP_RANGE from .params import DEFAULT_BACKGROUND_COLOR, DEFAULT_SPOT_SIZE, DEFAULT_PROJECTION, DEFAULT_ALPHA, DEFAULT_LINEWIDTH from .params import Projection, VALID_COLORS, HEX_COLOR_PATTERN, NFT_STORAGE_API, NFT_STORAGE_LINK, OVERVIEW -from .params import DATA_TYPE_ERROR, CONFIG_TYPE_ERROR, PLOT_DATA_ERROR, CONFIG_NO_STR_FUNCTION_ERROR +from .params import DATA_TYPE_ERROR, DATA_FORMAT_ERROR, CONFIG_TYPE_ERROR, CONFIG_FORMAT_ERROR, PLOT_DATA_ERROR, CONFIG_NO_STR_FUNCTION_ERROR from .params import NO_FIG_ERROR_MESSAGE, FIG_SAVE_SUCCESS_MESSAGE, NFT_STORAGE_SUCCESS_MESSAGE, SAVE_NO_DATA_ERROR -from .params import INVALID_COLOR_TYPE_ERROR +from .params import INVALID_COLOR_TYPE_ERROR, COLOR_SIZE_ERROR from .params import BOTH_COLOR_COMPLEMENT_WARNING, COLOR_NOT_FOUND_WARNING from .params import DATA_SAVE_SUCCESS_MESSAGE, SEED_LOWER_BOUND, SEED_UPPER_BOUND from .params import ELEMENTS_LIST, ARGUMENTS_LIST, OPERATORS_LIST, RANDOM_COEF_LIST @@ -27,17 +30,22 @@ def random_equation_gen(): :return: equation as str """ - num_elements = random.randint(2, len(ELEMENTS_LIST) + 3) + num_elements = random.randint(1, len(ELEMENTS_LIST)) result = "" index = 1 random_coef = random.choice(RANDOM_COEF_LIST) while(index <= num_elements): argument = random.choice(ARGUMENTS_LIST) + if random.randint(0, 1) == 1: + argument = random.choice(ELEMENTS_LIST).format( + random_coef, argument) result = result + \ random.choice(ELEMENTS_LIST).format(random_coef, argument) if index < num_elements: result = result + random.choice(OPERATORS_LIST) index = index + 1 + if random.randint(0, 1) == 1: + result = random.choice(ELEMENTS_LIST).format(random_coef, result) return result @@ -106,7 +114,7 @@ def is_valid_color(color): :type color: any format :return: result as bool """ - if color == None: + if color is None: return True try: _ = matplotlib.colors.to_hex(color) @@ -154,6 +162,29 @@ def filter_color(color, bgcolor): return color, bgcolor +def filter_cmap(cmap): + """ + Filter given cmap. + + :param cmap: color map + :type cmap: matplotlib.colors.Colormap or list of colors + :return: filtered version of cmap + """ + if isinstance(cmap, str): + cmap = cm.get_cmap(cmap, 256) + if type(cmap) == matplotlib.colors.Colormap: + cmap = cm.get_cmap(cmap.__getattribute__("name")) + if isinstance(cmap, matplotlib.colors.ListedColormap): + return cmap + if isinstance(cmap, (matplotlib.colors.LinearSegmentedColormap)): + cmap = cmap(range(DEFAULT_CMAP_RANGE)) + return ListedColormap(cmap) + if isinstance(cmap, list): + cmap = list(map(select_color, cmap)) + return ListedColormap(cmap) + return None + + def select_color(color): """ Select color and return it. @@ -257,6 +288,7 @@ def plot_params_filter( g, color=None, bgcolor=None, + cmap=None, spot_size=None, size=None, projection=None, @@ -271,6 +303,8 @@ def plot_params_filter( :type color: str :param bgcolor: background color :type bgcolor: str + :param cmap: color map + :type cmap: matplotlib.colors.Colormap or list of colors :param spot_size: point spot size :type spot_size: float :param size: figure size @@ -287,7 +321,13 @@ def plot_params_filter( raise samilaPlotError(PLOT_DATA_ERROR.format(1)) if g.data2 is None: raise samilaPlotError(PLOT_DATA_ERROR.format(2)) - color, bgcolor = filter_color(color, bgcolor) + if isinstance(color, list): + if len(color) != len(g.data1): + raise samilaPlotError(COLOR_SIZE_ERROR) + bgcolor = select_color(bgcolor) + else: + color, bgcolor = filter_color(color, bgcolor) + cmap = filter_cmap(cmap) projection = filter_projection(projection) alpha = filter_float(alpha) linewidth = filter_float(linewidth) @@ -297,6 +337,8 @@ def plot_params_filter( color = g.color if bgcolor is None: bgcolor = g.bgcolor + if cmap is None: + cmap = g.cmap if spot_size is None: spot_size = g.spot_size if size is None: @@ -307,7 +349,8 @@ def plot_params_filter( alpha = g.alpha if linewidth is None: linewidth = g.linewidth - g.color, g.bgcolor, g.spot_size, g.size, g.projection, g.alpha, g.linewidth = color, bgcolor, spot_size, size, projection, alpha, linewidth + g.color, g.bgcolor, g.cmap, g.spot_size, g.size, g.projection, g.alpha, g.linewidth = \ + color, bgcolor, cmap, spot_size, size, projection, alpha, linewidth def generate_params_filter( @@ -405,6 +448,7 @@ def _GI_initializer(g, function1, function2): g.data2 = None g.color = DEFAULT_COLOR g.bgcolor = DEFAULT_BACKGROUND_COLOR + g.cmap = DEFAULT_CMAP g.spot_size = DEFAULT_SPOT_SIZE g.size = DEFAULT_IMAGE_SIZE g.projection = DEFAULT_PROJECTION @@ -463,6 +507,7 @@ def save_data_file(g, file_adr): data['plot'] = { "color": g.color, "bgcolor": g.bgcolor, + "cmap": _serialize_cmap(g.cmap), "spot_size": g.spot_size, "projection": g.projection, "alpha": g.alpha, @@ -506,6 +551,7 @@ def save_config_file(g, file_adr): data['plot'] = { "color": g.color, "bgcolor": g.bgcolor, + "cmap": _serialize_cmap(g.cmap), "spot_size": g.spot_size, "projection": g.projection, "alpha": g.alpha, @@ -592,6 +638,7 @@ def samila_help(): :return: None """ print(OVERVIEW) + print("Website : https://www.samila.site") print("Repo : https://github.com/sepandhaghighi/samila") @@ -613,6 +660,44 @@ def is_same_data(data1, data2, precision=10**-5): return all(is_same) +def _serialize_color(color): + """ + Serialize the given color to a json serializable object. + + :param color: given color + :type color: str or nd.array + :return: the serializable version of the color + """ + if isinstance(color, str): + return color + return list(color) + + +def _serialize_cmap(cmap): + """ + Serialize the cmap for saving. + + :param cmap: color map + :type cmap: matplotlib.colors.Colormap + :return: list of colors + """ + return list(map(_serialize_color, cmap.colors)) + + +def _load_cmap(config): + """ + Load the cmap from config. + + :param config: plot part configuration + :type config: dict or json + :return: ListedColormap from cmap + """ + if "cmap" not in config: + return DEFAULT_CMAP + cmap = config["cmap"] + return ListedColormap(cmap) + + def load_data(g, data): """ Load data file. @@ -627,12 +712,15 @@ def load_data(g, data): data = json.load(data) g.data1 = data.get('data1') g.data2 = data.get('data2') + if g.data1 is None or g.data2 is None: + raise samilaDataError(DATA_FORMAT_ERROR) if 'matplotlib_version' in data: g.matplotlib_version = data['matplotlib_version'] plot_config = data.get("plot") if plot_config is not None: g.color = plot_config.get("color", DEFAULT_COLOR) g.bgcolor = plot_config.get("bgcolor", DEFAULT_BACKGROUND_COLOR) + g.cmap = _load_cmap(plot_config) g.spot_size = plot_config.get("spot_size", DEFAULT_SPOT_SIZE) g.projection = plot_config.get("projection", DEFAULT_PROJECTION) g.alpha = plot_config.get("alpha", DEFAULT_ALPHA) @@ -656,6 +744,8 @@ def load_config(g, config): data = json.load(config) g.function1_str = data.get("f1") g.function2_str = data.get("f2") + if g.function1_str is None or g.function2_str is None: + raise samilaConfigError(CONFIG_FORMAT_ERROR) if 'matplotlib_version' in data: g.matplotlib_version = data['matplotlib_version'] generate_config = data.get("generate") @@ -668,6 +758,7 @@ def load_config(g, config): if plot_config is not None: g.color = plot_config.get("color", DEFAULT_COLOR) g.bgcolor = plot_config.get("bgcolor", DEFAULT_BACKGROUND_COLOR) + g.cmap = _load_cmap(plot_config) g.spot_size = plot_config.get("spot_size", DEFAULT_SPOT_SIZE) g.projection = plot_config.get("projection", DEFAULT_PROJECTION) g.alpha = plot_config.get("alpha", DEFAULT_ALPHA) diff --git a/samila/genimage.py b/samila/genimage.py index ab7c23e..064b7a2 100644 --- a/samila/genimage.py +++ b/samila/genimage.py @@ -79,8 +79,7 @@ class GenerativeImage: self.data1 = [] self.data2 = [] range1 = list(float_range(self.start, self.stop, self.step)) - range2 = list(float_range(self.start, self.stop, self.step)) - range_prod = list(itertools.product(range1, range2)) + range_prod = list(itertools.product(range1, range1)) calc_exception = False for point in range_prod: if not fill_data(self, point): @@ -92,6 +91,7 @@ class GenerativeImage: self, color=None, bgcolor=None, + cmap=None, spot_size=None, size=None, projection=None, @@ -104,6 +104,8 @@ class GenerativeImage: :type color: str :param bgcolor: background color :type bgcolor: str + :param cmap: color map + :type cmap: matplotlib.colors.Colormap or list of colors :param spot_size: point spot size :type spot_size: float :param size: figure size @@ -120,6 +122,7 @@ class GenerativeImage: self, color, bgcolor, + cmap, spot_size, size, projection, @@ -134,6 +137,7 @@ class GenerativeImage: self.data1, alpha=self.alpha, c=self.color, + cmap=self.cmap, s=self.spot_size, lw=self.linewidth) ax.set_axis_off() diff --git a/samila/params.py b/samila/params.py index ec07031..2fa87e7 100644 --- a/samila/params.py +++ b/samila/params.py @@ -3,8 +3,9 @@ import math from enum import Enum from matplotlib import colors as mcolors +from matplotlib import cm -SAMILA_VERSION = "0.8" # pragma: no cover +SAMILA_VERSION = "0.9" # pragma: no cover OVERVIEW = ''' Samila is a generative art generator written in Python, Samila let's you @@ -18,6 +19,8 @@ DEFAULT_STOP = math.pi DEFAULT_STEP = 0.01 DEFAULT_COLOR = "black" DEFAULT_BACKGROUND_COLOR = "white" +DEFAULT_CMAP = cm.get_cmap("viridis", 256) +DEFAULT_CMAP_RANGE = 256 DEFAULT_ALPHA = 0.1 DEFAULT_LINEWIDTH = 1.5 DEFAULT_IMAGE_SIZE = (10, 10) @@ -35,9 +38,12 @@ FIG_SAVE_SUCCESS_MESSAGE = "Everything seems good." DATA_SAVE_SUCCESS_MESSAGE = "Everything seems good." NO_FIG_ERROR_MESSAGE = "No figure was found. First run `generate` and `plot` methods." DATA_TYPE_ERROR = "Provided data file is not supported. It should be either file or io.IOBase." +DATA_FORMAT_ERROR = "Provided data file is not supported. It should include data1 and data2." CONFIG_TYPE_ERROR = "Provided config file is not supported. It should be either file or io.IOBase." +CONFIG_FORMAT_ERROR = "Provided config file is not supported. It should include f1 and f2." CONFIG_NO_STR_FUNCTION_ERROR = "Config file can't be saved. At least one of the function1_str or function2_str is None." PLOT_DATA_ERROR = "Plotting process can't be Done because data{0} is empty. Use generate method first." +COLOR_SIZE_ERROR = "Color list size is not equal to the data size." SAVE_NO_DATA_ERROR = "Data file can't be saved. At least one of the data1 or data2 is None." INVALID_COLOR_TYPE_ERROR = "Given color/bgcolor type is not supported." MATPLOTLIB_VERSION_WARNING = "Source matplotlib version({0}) is different from yours, plots may be different." @@ -98,6 +104,8 @@ ARGUMENTS_LIST = [ "y**2", "(x**2)*y", "(y**2)*x", + "(y**2)+(x**2)", + "(y**2)-(x**2)", "(x**2)*(y**3)", "(x**3)*(y**2)", "x*(y**3)", diff --git a/setup.py b/setup.py index 50076d4..bb60154 100644 --- a/setup.py +++ b/setup.py @@ -32,14 +32,14 @@ def read_description(): setup( name='samila', packages=['samila'], - version='0.8', + version='0.9', description='Generative ART', long_description=read_description(), long_description_content_type='text/markdown', author='Samila Development Team', - author_email='info@4r7.ir', - url='https://github.com/sepandhaghighi/samila', - download_url='https://github.com/sepandhaghighi/samila/tarball/v0.8', + author_email='info@samila.site', + url='https://www.samila.site', + download_url='https://github.com/sepandhaghighi/samila/tarball/v0.9', keywords="generative-art art nft file nft-storage", project_urls={ 'Source': 'https://github.com/sepandhaghighi/samila', diff --git a/test/error_test.py b/test/error_test.py index fd8008f..5daac68 100644 --- a/test/error_test.py +++ b/test/error_test.py @@ -24,8 +24,9 @@ Traceback (most recent call last): ... samila.errors.samilaPlotError: Plotting process can't be Done because data1 is empty. Use generate method first. >>> with open("data.json", 'w') as fp: -... json.dump({'data1': [0]}, fp) +... json.dump({'data1': [0], 'data2': [0]}, fp) >>> g = GenerativeImage(data=open('data.json', 'r')) +>>> g.data2 = None >>> g.save_data() Traceback (most recent call last): ... @@ -39,6 +40,10 @@ samila.errors.samilaPlotError: Plotting process can't be Done because data2 is e Traceback (most recent call last): ... samila.errors.samilaPlotError: Given color/bgcolor type is not supported. +>>> g.plot(color=[0]) +Traceback (most recent call last): + ... +samila.errors.samilaPlotError: Color list size is not the equal to the data size. >>> g = GenerativeImage(lambda x,y: x, lambda x,y: y) >>> result = g.save_config() Traceback (most recent call last): @@ -57,5 +62,30 @@ samila.errors.samilaPlotError: Given color/bgcolor type is not supported. Traceback (most recent call last): ... samila.errors.samilaPlotError: Given color/bgcolor type is not supported. +>>> with open("data.json", 'w') as fp: +... json.dump({'data1': [0]}, fp) +>>> g = GenerativeImage(data=open('data.json', 'r')) +Traceback (most recent call last): + ... +samila.errors.samilaDataError: Provided data file is not supported. It should include data1 and data2. +>>> with open("data.json", 'w') as fp: +... json.dump({'data2': [0]}, fp) +>>> g = GenerativeImage(data=open('data.json', 'r')) +Traceback (most recent call last): + ... +samila.errors.samilaDataError: Provided data file is not supported. It should include data1 and data2. +>>> with open("config.json", 'w') as fp: +... json.dump({'f1': "x"}, fp) +>>> g = GenerativeImage(config=open('config.json', 'r')) +Traceback (most recent call last): + ... +samila.errors.samilaConfigError: Provided config file is not supported. It should include f1 and f2. +>>> with open("config.json", 'w') as fp: +... json.dump({'f2': "x"}, fp) +>>> g = GenerativeImage(config=open('config.json', 'r')) +Traceback (most recent call last): + ... +samila.errors.samilaConfigError: Provided config file is not supported. It should include f1 and f2. >>> os.remove('data.json') +>>> os.remove('config.json') """ diff --git a/test/function_test.py b/test/function_test.py index 1cfa537..52d3377 100644 --- a/test/function_test.py +++ b/test/function_test.py @@ -88,5 +88,6 @@ create arts based on many thousand points. The position of every single point is calculated by a formula, which has random parameters. Because of the random numbers, every image looks different. +Website : https://www.samila.site Repo : https://github.com/sepandhaghighi/samila """ diff --git a/test/overall_test.py b/test/overall_test.py index e96a30e..2d13e5e 100644 --- a/test/overall_test.py +++ b/test/overall_test.py @@ -6,6 +6,7 @@ >>> import pickle >>> import socket >>> import json +>>> from matplotlib.colors import Colormap, ListedColormap >>> def guard(*args, **kwargs): ... raise Exception("No internet connection!") >>> from samila import GenerativeImage, Projection @@ -196,16 +197,6 @@ True >>> g.spot_size == g_.spot_size True >>> with open("config.json", 'w') as fp: -... json.dump({'f1': 'x'}, fp) ->>> g = GenerativeImage(config=open("config.json", 'r')) ->>> g.function1_str -'x' ->>> with open("config.json", 'w') as fp: -... json.dump({'f2': 'x'}, fp) ->>> g = GenerativeImage(config=open("config.json", 'r')) ->>> g.function2_str -'x' ->>> with open("config.json", 'w') as fp: ... json.dump({'f1': 'y', 'f2': 'x'}, fp) >>> g = GenerativeImage(config=open("config.json", 'r')) >>> g.function1_str @@ -222,6 +213,46 @@ False [0] >>> g.data2 [1] +>>> with open("data.json", 'w') as fp: +... json.dump({'data1': [0], 'data2': [1], 'plot':{}}, fp) +>>> g = GenerativeImage(data=open("data.json", 'r')) +>>> with open("config.json", 'w') as fp: +... json.dump({'f1': "x", 'f2': "y", 'plot':{}}, fp) +>>> g = GenerativeImage(config=open("config.json", 'r')) +>>> g = GenerativeImage() +>>> g.generate() +>>> cm = Colormap(name="Purples") +>>> g.plot(cmap=cm) +>>> result = g.save_config("config.json") +>>> result["status"] +True +>>> g_ = GenerativeImage(config=open("config.json", 'r')) +>>> (g_.cmap.colors == g.cmap.colors).all() +True +>>> cm = ["black", [0.6, 0.2, 0.2, 1], [0.5, 0.2, 0.2, 1], [0.4, 0.2, 0.2, 1], "yellow", [0.2, 0.2, 0.2, 1],] +>>> g.plot(cmap=cm) +>>> result = g.save_config("config.json") +>>> result["status"] +True +>>> g_ = GenerativeImage(config=open("config.json", 'r')) +>>> g_.cmap.colors == g.cmap.colors +True +>>> g.plot(cmap="Purples") +>>> cm = Colormap(name="viridis") +>>> g.plot(cmap=cm) +>>> cmap = [[0.7, 0.2, 0.2, 1], [0.6, 0.2, 0.2, 1], [0.3, 0.2, 0.2, 1], [0.2, 0.2, 0.2, 1]] +>>> g.plot(cmap=ListedColormap(cmap)) +>>> g = GenerativeImage() +>>> g.generate() +>>> g.plot(cmap=cmap, color=g.data1) +>>> result = g.save_data("data.json") +>>> result["status"] +True +>>> g_ = GenerativeImage(data=open("data.json", "r")) +>>> g_.plot() +>>> g_.cmap.colors == g.cmap.colors +True +>>> g.plot(color=g.data1) >>> g_ = GenerativeImage() >>> del(g) >>> del g_.data1