# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['motionpicture']

package_data = \
{'': ['*']}

install_requires = \
['ConfigArgParse>=1.0.0,<2.0.0',
 'ffmpeg-python>=0.1.0,<0.2.0',
 'tqdm>=4.0.0,<5.0.0']

entry_points = \
{'console_scripts': ['mopi = motionpicture.mopi:main']}

setup_kwargs = {
    'name': 'motionpicture',
    'version': '0.1.2',
    'description': 'Make animations with Python',
    'long_description': '<p align="center">\n<img src="https://github.com/Sbozzolo/motionpicture/raw/master/logo.png" width="636" height="131">\n</p>\n\n[![GPLv3\nlicense](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html)\n![Tests and documentation](https://github.com/Sbozzolo/motionpicture/workflows/Tests/badge.svg)\n[![codecov](https://codecov.io/gh/Sbozzolo/motionpicture/branch/master/graph/badge.svg?token=z7jvNNdwVS)](https://codecov.io/gh/Sbozzolo/motionpicture)\n\n# Introduction\n\n`motionpicture` is a Python library to simplify the creation of videos out of\nindividual frames. With `motionpicture`, you just have to specify how to produce\na generic frame, and the package will do everything else for you. In\n`motionpicture`, your code can be configured via command-line or text files:\nturning your code into a plug-in for `motionpicture` is trivial, so you will be\nable to reuse your code with ease.\n\n# Examples\n\nThere are two important ingredients to use `motionpicture`: `mopi`, and a\n_movie_ file. `mopi` is a command-line utility that comes when you install this\npackage. It will be your main interface to `motionpicture` and it has a\ncomprehensive `--help` function. A _movie_ file is a recipe on how to produce a\ngeneric frame. With few small restrictions, you have full control over this file\n(more info in section [Movie files](#movie-files)).\n\nIn these examples we are going to use `matplotlib` to do the plotting, but you\nare completely free to generate frames with any Python package you wish.\n\n## Unveiling a sine wave\n\nIn this example, we show how to use `mopi` to generate the following video.\n![sine_wave](https://github.com/Sbozzolo/motionpicture/raw/master/sine_wave.gif)\n\nTo produce this video, we need the following _movie_ file.\n``` python\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nclass MOPIMovie:\n    def __init__(self, _args):\n        self.times = np.linspace(0, 10, 100)\n        self.values = np.sin(self.times)\n\n    def get_frames(self):\n        # Here we tell motionpicture what we consider a frame\n        return range(self.times)\n\n    def make_frame(self, path, frame_number):\n        # Here we plot a specific frame\n        plt.clf()\n        plt.plot(self.times[:frame_number], self.values[:frame_number])\n        plt.xlim([0, self.times[-1]])\n        plt.ylim([-1, 1])\n        plt.savefig(path)\n```\nAssuming this file is saved in `sin_wave.py`, we run\n\n``` sh\nmopi -m sin_wave.py -o frames_dir --parallel\n```\nThis is produce the individual frames in a folder `frames_dir` using all the CPUs\navailable on your machine. Then, it will glue the frames together in a video\nthat has the default name of `video.mp4`. If you want to change name, or other\nproperties (e.g., the fps), you can add options to `mopi`\n``` sh\nmopi -m sin_wave.py -o frames_dir --parallel --fps 10 --movie-name sin_wave\n```\nThis will produce a `sin_wave.mp4` video with 10 frames per second instead\n\n## Unveiling a sine wave with controllable frequency\n\nLet us continue on the example of the sine wave, and let us assume that we want\nto explore different frequencies.\n\nWe can edit the previous _movie_ file adding a `mopi_add_custom_options`\nfunction:\n\n``` python\ndef mopi_add_custom_options(parser):\n    """Add command-line options specific to this movie."""\n    parser.add_argument(\n        "-f",\n        "--frequency",\n        default=1,\n        type=int,\n        help="Frequency of the sine wave (default: %(default)s)",\n    )\n```\nThen, we edit the `__init__` function too:\n``` python\ndef __init__(self, args):\n    self.times = np.linspace(0, 10, 100)\n    self.values = np.sin(args.frequency * self.times)\n```\n_Movie_ files have to have an `__init__` that takes two arguments. The second\nis a `Namespace` that contains all the controllable options. These arguments\ncan be passed via command-line or configuration file.\n``` sh\nmopi -m sin_wave.py -o frames_dir --parallel --frequency 3\n```\nThis command will produce the following video.\n\n![sine_wave_fast](https://github.com/Sbozzolo/motionpicture/raw/master/sine_wave_fast.gif)\n\nAlternatively, you can put any of arguments in a config file `conf`, for example:\n\n``` text\noutdir: frames_dir\nfrequency: 3\n```\nConfig files support several syntaxes. Once you have the file, just call\n``` sh\nmopi sin_wave.py -c conf\n```\nYou can use config files and command-line options at the same time, but in case\nof conflict, the command-line arguments have the precedence.\n\n## Unveiling data in an arbitrary file\n\nNow that you have seen that you can control movies via command-line, it is time\nto introduce you to the plugin system in `motionpicture`.\n\nSuppose we have two-column files with time series data, we can modify the _movie_\nfile used in the previous example to animate those files, specifying which one\nat run-time.\n\n``` python\ndef mopi_add_custom_options(parser):\n    """Add command-line options specific to this movie."""\n    parser.add_argument(\n        "-f",\n        "--file",\n        required=True,\n        help="File to plot",\n    )\n```\nThen, we `import numpy as np` and edit the `__init__` function too:\n``` python\ndef __init__(self, args):\n    self.times, self.values = np.loadtxt(args.file).T\n    self.y_min, self.y_max = np.amin(self.value), np.amax(self.value)\n```\nWe computed the minimum and maximum of the value so that we can adjust the y axis\nrange. The  `make_frame` method will be the same, with the exception that we change\nthe `plt.ylim([-1, 1])` line to `plt.ylim([self.y_min, self.y_max])`.\n\nWe can save this file as `plot_timeseries` and call `mopi`:\n``` sh\nmopi -m plot_timeseries -o frames -f my_file.dat\n```\nOf course, we can add as many options as we wish to control the output. For instance,\nwe may want to add a switch to use logarithmic axes instead. The class\n`MOPIMovie` has full access to the user-supplied options, so you can do anything\nyou wish.\n\nWe did not hard-code anything in `plot_timeseries`, so the code will work for\nany dataset. However, if we want to use this file again, but in a different\nfolder, we would have to copy it over, since `mopi -m` expects the path of the\n_movie_ file. Alternatively, we can copy `plot_timeseries` to a specific folder\nof our choice, for example `~/.mopi_videos`. Then, we can set the environment\nvariable `MOPI_MOVIES_DIR` to be `~/.mopi_videos`, and `mopi` will be able to\nfind `plot_timeseries` from anywhere in your filesystem. In this case, you can\nsimply call:\n``` sh\nmopi plot_timeseries -o frames -f my_other_file.dat\n```\nEssentially, `plot_timeseries` became a plugin for `motionpicture` and you can\nanimate any data without having to write new code. This is one of the greatest\nstrengths of `motionpicture`, as it encourages you to write generic code that you\ncan easily reuse.\n\n# Installation\n\n`motionpicture` is available on PyPI. You can install it with `pip`:\n\n``` sh\npip3 install motionpicture\n```\n\nTo produce the final video, you have to have `ffmpeg` installed. Without\n`ffmpeg`, you will not be able to glue together the frames, but you can still\nuse `motionpicture` to render the frames.\n\n# _Movie_ files\n\nIn the language of `motionpicture`, a _movie_ file is a recipe on how to\ngenerate an individual frame. It is completely up to you how you do that, but\n`motionpicture` imposes some minimum requirements:\n\n- It has to be a valid Python 3 file\n- It has to contain a class `MOPIMovie` with a method `make_frame` and a method\n  `get_frames`.\n- The method `__init__` has to take two arguments.\n- The method `get_frames` has to return an iterable (e.g., a list) that\n  identifies each frame. The elements of this iterable are passed as the `frame`\n  argument to `make_frame`. \n- The method `make_frame` has to take two arguments, the `path` of the output of\n  the frame, and `frame`, the value that identifies frame (typically the frame\n  number). `path` is where the image has to be saved. You are in charge of\n  saving the image using the save method of your plotting package.\n\nOther than these requirements, you can do anything you want in the _movie_ file\n(e.g., you can add more methods, functions, classes...).\n\n> :warning: Due to its own nature, `motionpicture` has to execute any code that\n>           you supply. Do not use `motionpicture` with codes you do not trust!\n\n# Development\n\nWe use:\n* [Poetry](python-poetry.org) to manage dependencies, build, and publish\n  `motionpicture`.\n* [Black](https://github.com/psf/black) for formatting the code (with 89\n  columns).\n* [pytest](https://pytest.org) for unit tests (with `pytest-cov` for test\n  coverage).\n* GitHub actions for continuous integration.\n\nWe are happy to accept contributions.\n\n# Credits\n\nThe idea for `motionpicture` originated from the `SimVideo` package developed by\nWolfgang Kastaun.\n\n',
    'author': 'Gabriele Bozzola',
    'author_email': 'gabrielebozzola@arizona.edu',
    'maintainer': 'Gabriele Bozzola',
    'maintainer_email': 'gabrielebozzola@arizona.edu',
    'url': 'https://github.com/sbozzolo/motionpicture',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
