diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0786596b4..ca401a241 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,221 +1,299 @@ -# Contribution guide +# Contributing Guide -Contributions are welcome! :smile: +`fastplotlib` is a next-generation plotting library built on top of the `pygfx` rendering engine that leverages modern +GPU hardware and new graphics APIs to build large-scale scientific visualizations. We welcome and encourage contributions +from everyone! :smile: -## Installation +This guide explains how to contribute: if you have questions about the process, please +reach out on [GitHub Discussions](https://github.com/fastplotlib/fastplotlib/discussions). + +> **_NOTE:_** If you are already familiar with contributing to open-source software packages, +> please check out the [quick guide](#contributing-quick-guide)! + +## General Guidelines + +Developers are encouraged to contribute to various areas of development. This could include the addition of new features (e.g. +graphics or selector tools), bug fixes, or the addition of new examples to the [examples gallery](https://fastplotlib.readthedocs.io/en/latest/_gallery/index.html). +Enhancements to documentation and the overall readability of the code are also greatly appreciated. + +Feel free to work on any section of the code that you believe you can improve. More importantly, remember to thoroughly test all +your classes and functions, and to provide clear, detailed comments within your code. This not only aids others in using the library, +but also facilitates future maintenance and further development. + +For more detailed information about `fastplotlib` modules, including design choices and implementation details, visit the +[`For Develeopers`](https://fastplotlib.readthedocs.io/en/latest/developer_notes/index.html) section of the package documentation. + +## Contributing to the code + +### Contribution workflow cycle + +In order to contribute, you will need to do the following: + +1) Create your own branch +2) Make sure that tests pass +3) Open a Pull Request + +The `fastplotlib` package follows the [Git feature branch](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow) workflow. In essence, `main` is the primary branch to which no one is allowed to +push directly. All development happens in separate feature branches that are then merged into `main` once we have determined they are ready. When enough changes have accumulated, a new release is +generated. This process includes adding a new tag to increment the version number and uploading the new release to PyPI. + +### Creating a development environment + +You will need a local installation of `fastplotlib` which keeps up-to-date with any changes you make. To do so, you will need to fork and clone `fastplotlib` before checking out a new branch. 1. Fork the repo to your own GitHub account, click the "Fork" button at the top: ![image](https://github.com/kushalkolar/fastplotlib/assets/9403332/82612021-37b2-48dd-b7e4-01a919535c17) -2. Clone the repo and install according to the development instructions. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. +2. We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need +to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. If you already have `git-lfs` +installed, ignore this step. + +3. Clone the repo. Replace the `YOUR_ACCOUNT` in the repo URL to the fork on your account. ```bash git clone https://github.com/YOUR_ACCOUNT/fastplotlib.git cd fastplotlib +``` + +> **_NOTE:_** If you cloned the repo before installing `git-lfs`, you can run `git lfs pull` at any +> time to download the files stored on LFS + +4. Install `fastplotlib` in editable mode with developer dependencies +```bash # install all extras in place pip install -e ".[notebook,docs,tests]" ``` -> If you cloned the repo before installing `git-lfs`, you can run `git lfs pull` at any -> time to download the files stored on LFS +5. Add the upstream remote branch: -3. Checkout the `main` branch, and then checkout your feature or bug fix branch, and run tests: +```bash +git remote add upstream https://github.com/fastplotlib/fastplotlib +``` -If your contributions modify how visualizations look, see the "Tests in detail" section at the very bottom. +At this point you have two remotes: `origin` (your fork) and `upstream` (the official fastplotlib org version). You won't have permission to push to upstream (only `origin`), but +this makes it easy to keep your `fastplotlib` up-to-date with the official fastplotlib org version by pulling from upstream: `git pull upstream`. -```bash -cd fastplotlib +### Creating a new branch + +As mentioned previously, each feature in `fastplotlib` is worked on in a separate branch. This allows multiple people to develop multiple features simultaneously, without interfering with each other's work. To create +your own branch, run the following from within your `fastplotlib` directory: +```bash +# switch to the main branch on your local copy git checkout main -# checkout your new branch from main -git checkout -b my-new-feature-branch +# update your local copy from your fork +git pull origin main -# make some changes, lint with black -black . +# sync your local copy with upstream main +git pull upstream main + +# update your fork's main branch with any changes from upstream +git push origin main + +# create and switch to a new branch, where you'll work on your new feature +git checkout -b my_feature_branch +``` + +After you have made changes on this branch, add and commit them when you are ready: + +```bash +# lint your code +black . # run tests from the repo root dir +WGPU_FORCE_OFFSCREEN=1 pytest tests/ + +# desktop examples pytest -v examples + +# notebook examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ -# add your changed files, do not add any changes from screenshot diff dirs +# add your changed files, do not add any changes from the screenshot diff directory git add my_changed_files -# commit changes -git commit -m "my new feature" +# commit your changes +git commit -m "A one-line message explaining the changes made" -# push changes to your fork -git push origin my-new-feature-branch +# push to the remote origin +git push origin my_feature_branch ``` +> **_NOTE:_** If your contributions modify how visualizations _look_, see the [Testing details](#testing-details) section at the very bottom. -4. Finally make a **draft** PR against the `main` branch. When you think the PR is ready, mark it for review to trigger tests using our CI pipeline. If you need to make changes, please set the PR to a draft when pushing further commits until it's ready for review scion. We will get back to your with any further suggestions! +> **_NOTE:_** If your contributions modify the API, you must regenerate the API docs before making a PR, see +> the [Documenation](#documentation) section below. -## How fastplotlib works +### Contributing your changes back to `fastplotlib` -Fastplotlib uses the [`pygfx`](https://github.com/pygfx/pygfx) rendering engine to give users a high-level scientific -plotting library. Some degree of familiarity with [`pygfx`](https://github.com/pygfx/pygfx) or rendering engines may -be useful depending on the type of contribution you're working on. +You can make any number of changes on your branch. Once you are happy with your changes, add tests to check that they run correctly and add +documentation to properly note your changes. +See below for details on how to [add tests](#adding-tests) and properly [document](#adding-documentation) your code. -There are currently 2 major subpackages within `fastplotlib`, `layouts` and `graphics`. The user-facing public -class within `layouts` is `Figure`. A user is intended to create a `Figure`, and -then add *Graphics* to subplots within that `Figure`. +Now you are ready to make a Pull Request. You can open a pull request by clicking on the big `Compare & pull request` button that appears at the top of the `fastplotlib` repo +after pushing to your branch (see [here](https://intersect-training.org/collaborative-git/03-pr/index.html) for a tutorial). -### Graphics +> **_NOTE:_** Please make sure that you initially make your PR as a **draft** PR against the `main` branch. When you think the PR is ready, mark +> it for review to trigger tests using our CI pipeline. If you need to make changes, please set the PR back to a draft when pushing further +> commits until it is ready for review again. -A `Graphic` is something that can be added to a `PlotArea` (described in detail in a later section). All the various -fastplotlib graphics, such as `ImageGraphic`, `ScatterGraphic`, etc. inherit from the `Graphic` base class in -`fastplotlib/graphics/_base.py`. It has a few properties that mostly wrap `pygfx` `WorldObject` properties and transforms. -These might change in the future (ex. `Graphic.position_x` etc.). +Your pull request should include the following: +- A summary including information on what you changed and why +- References to relevant issues or discussions +- Special notice to any portion of your changes where you have lingering questions (e.g., "was this the right way to implement this?") or +want reviewers to pay special attention to -All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from -plots, ex: `subplot["some_image"]`. +Next, we will be notified of the pull request and will read it over. We will try to give an initial response quickly, and then do a longer in-depth +review, at which point you will probably need to respond to our comments, making changes as appropriate. We will then respond again, and proceed +in an iterative fashion until everyone is happy with the proposed changes. -All graphics contain a `world_object` property which is just the `pygfx.WorldObject` that this graphic uses. Fastplotlib -keeps a *private* global dictionary of all `WorldObject` instances and users are only given a weakref proxy to this world object. -This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . -If you are curious or have more questions on garbage collection in fastplotlib you're welcome to post an issue :D. +Once your changes are integrated, you will be added as a GitHub contributor. Thank you for being +a part of `fastplotlib`! -#### Graphic properties +### Style Guide -Graphic properties are all evented, and internally we called these "graphic features". They are the various -aspects of a graphic that the user can change. -The "graphic features" subpackage can be found at `fastplotlib/graphics/_features`. As we can see this -is a private subpackage and never meant to be accessible to users.. +As far as code style, please adhere to the following guidelines: -##### LineGraphic +- Longer, descriptive names are preferred (e.g., `x` is not an appropriate name for a variable), especially for anything user-facing, +such as methods, attributes, or arguments +- Any public method, property, or attribute must have complete type-annotated docstrings (see below for details). Private methods or +attributes do not need to have a complete docstring, but they probably should. -For example let's look at `LineGraphic` in `fastplotlib/graphics/line.py`. Every graphic has a class variable called -`_features` which is a set of all graphic properties that are evented. It has the following evented properties: -`"data", "colors", "cmap", "thickness"` in addition to properties common to all graphics, such as `"name", "offset", "rotation", and "visible"` +### Releases -Now look at the constructor for the `LineGraphic` base class `PositionsGraphic`, it first creates an instance of `VertexPositions`. -This is a class that manages vertex positions buffer. It defines the line, and provides additional useful functionality. -For example, every time that the `data` is changed, the new data will be marked for upload to the GPU before the next draw. -In addition, event handlers will be called if any event handlers are registered. +We create releases on GitHub and distribute via [pypi](https://pypi.org/), and try to follow [semantic versioning](https://semver.org/): -`VertexColors`behaves similarly, but it can perform additional parsing that can create the colors buffer from different -forms of user input. For example if a user runs: `line_graphic.colors = "blue"`, then `VertexColors.__setitem__()` will -create a buffer that corresponds to what `pygfx.Color` thinks is "blue". Users can also take advantage of fancy indexing, -ex: `line_graphics.colors[bool_array] = "red"` :smile: +> Given a version number MAJOR.MINOR.PATCH, increment the: +> 1. MAJOR version when you make incompatible API changes +> 2. MINOR version when you add functionality in a backward compatible manner +> 3. PATCH version when you make backward compatible bug fixes -`LineGraphic` also has a `VertexCmap`, this manages the line `VertexColors` instance to parse colormaps, for example: -`line_graphic.cmap = "jet"` or even `line_graphic.cmap[50:] = "viridis"`. +To release a new version, we [create a GitHub release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) with a new tag incrementing the version as described above. +Creating the GitHub release will trigger the deployment to pypi, via our `deploy` action (found in `.github/workflows/pypi-publish.yml`). +The built version will grab the version tag from the GitHub release, using [setuptools_scm](https://github.com/pypa/setuptools_scm). -`LineGraphic` also has a `thickness` property which is pretty simple, and `DeletedFeature` which is useful if you need -callbacks to indicate that the graphic has been deleted (for example, removing references to a graphic from a legend). +### Testing -Other graphics have properties that are relevant to them, for example `ImageGraphic` has `cmap`, `vmin`, `vmax`, -properties unique to images. +#### Testing Details -#### Selectors +As a plotting library we require two layers of testing. 1) We use a backend test suite that verifies the basic functionality of buffer managers, +graphics, layouts, etc., and 2) another test suite which verifies that the library renders plots that are visually correct. -Selectors are a fairly new subpackage at `fastplotlib/graphics/selectors` which is likely to change significantly -after https://github.com/pygfx/pygfx/pull/665 . This subpackage contains selection tools, such as line selectors -(horizontal or vertical lines that can be moved), linear region selectors, and a primitive polygon drawing selection tool. -All selector tools inherit from `BaseSelector` in `graphics/selectors/_base_selector.py` but this is likely to change -after the aforementioned `Input` class PR in `pygfx` and after https://github.com/fastplotlib/fastplotlib/pull/413 . +In order to do this, each example within the `examples` directory is run and an image of the canvas is taken and compared +with a ground-truth screenshot that we have manually inspected. Ground-truth images are stored using `git-lfs`. -### Layouts +The ground-truth images are located in: -#### PlotArea +``` +examples/desktop/screenshots +examples/notebooks/screenshots +``` -This is the main base class within layouts. Subplots within a `Figure` and `Dock` areas within a `Subplot`, -inherit from `PlotArea`. +The tests will produce slightly different imperceptible (to a human) results on different hardware when compared to the +ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and +ground-truth image are within that tolerance the test will pass. -`PlotArea` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: +Some feature development may require the ground-truth screenshots to be updated. In the event that your changes require +this, please do the following: -* scene - instance of `pygfx.Scene` -* canvas - instance of `WgpuCanvas` -* renderer - instance of `pygfx.WgpuRenderer` -* viewport - instance of `pygfx.Viewport` -* camera - instance of `pygfx.PerspectiveCamera`, we always just use `PerspectiveCamera` and just set `camera.fov = 0` for orthographic projections -* controller - instance of `pygfx.Controller` +1. Download the regenerated screenshots from the [`fastplotlib` GitHub Actions page](https://github.com/fastplotlib/fastplotlib/actions/workflows/screenshots.yml) for your specific PR -Abstract method that must be implemented in subclasses: +2. Replace only the screenshots that your PR changes in your local `fastplotlib` screenshots directories with those downloaded -* get_rect - musut return [x, y, width, height] that defines the viewport rect for this `PlotArea` +``` +examples/desktop/screenshots +examples/notebooks/screenshots +``` -Properties specifically used by subplots in a Figure: +3. Commit your new screenshots and push them to your branch to get picked up by `git-lfs` -* parent - A parent if relevant, used by individual `Subplots` in `Figure`, and by `Dock` which are "docked" subplots at the edges of a subplot. -* position - if a subplot within a Figure, it is the position of this subplot within the `Figure` +```bash +# add changes +git add examples/desktop/screenshots/ +git add examples/notebooks/screenshots/ + +# commit changes +git commit -m "update screenshots" -Other important properties: +# push changes +git push origin my_feature_branch +``` -* graphics - a tuple of weakref proxies to all `Graphics` within this `PlotArea`, users are only given weakref proxies to `Graphic` objects, all `Graphic` objects are stored in a private global dict. -* selectors - a tuple of weakref proxies to all selectors within this `PlotArea` -* legend - a tuple of weakref proxies to all legend graphics within this `PlotArea` -* name - plot areas are allowed to have names that the user can use for their convenience +#### Adding tests -Important methods: +Depending on the type of contribution you are making, new tests might need to be added to the repository. Unit tests for testing underlying functionality such as buffer managers, figure instantiation, and +more can be found in the `/tests` directory. However, we also test all of our desktop examples as well. -* add_graphic - add a `Graphic` to the `PlotArea`, append to the end of the `PlotArea._graphics` list -* insert_graphic - insert a `Graphic` to the `PlotArea`, insert to a specific position of the `PlotArea._graphics` list -* remove_graphic - remove a graphic from the `Scene`, **does not delete it** -* delete_graphic - delete a graphic from the `PlotArea`, performs garbage collection -* clear - deletes all graphics from the `PlotArea` -* center_graphic - center camera w.r.t. a `Graphic` -* center_scene - center camera w.r.t. entire `Scene` -* auto_scale - Auto-scale the camera w.r.t to the `Scene` +If you are adding a new example to the library, you will need to add the following comments to the top of your `.py` file in order to make sure it is both tested and added to the gallery. -In addition, `PlotArea` supports `__getitem__`, so you can do: `plot_area["graphic_name"]` to retrieve a `Graphic` by -name :smile: +```python +# test_example = true +# sphinx_gallery_pygfx_docs = 'screenshot' +``` -You can also check if a `PlotArea` has certain graphics, ex: `"some_image_name" in plot_area`, or `graphic_instance in plot_area` +### Documentation -#### Subplot +Documentation is a crucial part of open-source software and greatly influences the ability to use a codebase. As such, it is imperative that any new changes are +properly documented as outlined below. -This class inherits from `PlotArea` and `GraphicMethodsMixin`. +We use [`sphinx`](https://www.sphinx-doc.org/en/master/) for generating our documentation. In addition to this, we also use the [`sphinx-gallery`](https://sphinx-gallery.github.io/stable/index.html) +extension to build our examples gallery. -`GraphicMethodsMixin` is a simple class that just has all the `add_` methods. It is autogenerated by a utility script like this: +If you would like to build the documentation locally: ```bash -python scripts/generate_add_methods.py -``` +cd docs +# regenerate the api guide +python source/generate_api.py -Each `add_` method basically creates an instance of `Graphic`, adds it to the `Subplot`, and returns a weakref -proxy to the `Graphic`. +# build locally +make html +``` -Subplot has one property that is not in `PlotArea`: +#### Adding documentation -* docks: a `dict` of `PlotAreas` which are located at the "top", "right", "left", and "bottom" edges of a `Subplot`. By default their size is `0`. They are useful for putting things like histogram LUT tools. +All public-facing functions and classes should have complete docstrings, which start with a one-line short summary of the function, +a medium-length description of the function / class and what it does, and a complete description of all arguments and return values. +Docstrings should be comprehensive, providing the information necessary for a user to use the method or property without going through the code. -The key method in `Subplot` is an implementation of `get_rect` that returns the viewport rect for this subplot. +Private functions and classes should have sufficient explanation that other developers know what the function / class does and how to use it, +but do not need to be as extensive. -#### Figure +We follow the [numpydoc](https://numpydoc.readthedocs.io/en/latest/) conventions for docstring structure. -Now that we have understood `PlotArea` and `Subplot` we need a way for the user to create them! +### Contributing Quick Guide -A `Figure` contains a grid of subplot and has methods such as `show()` to output the figure. -`Figure.__init__` basically does a lot of parsing of user arguments to determine how to create -the subplots. All subplots within a `Figure` share the same canvas and use different viewports to create the subplots. +This section is a brief introduction to how to contribute to `fastplotlib`. It is intended for individuals who have prior experience with contributing +to open source software packages. -## Tests in detail +> **_NOTE:_** +> We use [git-lfs](https://git-lfs.com) for storing large files, such as ground-truths for tests, so you will need +> to [install it](https://github.com/git-lfs/git-lfs#installing) before cloning the repo. -Backend tests are in `tests/`, in addition as a plotting library CI pipeline produces things that -"look visually correct". Each example within the `examples` dir is run and an image of the canvas -is taken and compared with a ground-truth screenshot that we have manually inspected. -Ground-truth image are stored using `git-lfs`. +1) Fork and clone the repo -The ground-truth images are in: +2) Install locally with developer dependencies -``` -examples/desktop/screenshots -examples/notebooks/screenshots +```bash +# after cloning +cd fastplotlib +# install dev dependencies +pip install -e ".[tests, docs, notebook]" ``` -The tests will produce slightly different imperceptible (to a human) results on different hardware when compared to the -ground-truth. A small RMSE tolerance has been chosen, `0.025` for most examples. If the output image and -ground-truth image are within that tolerance the test will pass. - +3) Check out a feature branch from `main` -To run tests: +4) Lint codebase and make sure tests pass ```bash -# tests basic backend functionality -WGPU_FORCE_OFFSCREEN=1 pytest -v -s tests/ +# lint codebase +black . + +# run tests +# backend tests +WGPU_FORCE_OFFSCREEN=1 pytest tests/ # desktop examples pytest -v examples @@ -224,19 +302,9 @@ pytest -v examples FASTPLOTLIB_NB_TESTS=1 pytest --nbmake examples/notebooks/ ``` -If your contribution modifies a ground-truth test screenshot then replace the ground-truth image along with your PR and -also notify us of this in the PR. Likewise, if your contribution requires a new test or new ground-truth then include -this new image in your PR. +5) Update screenshots if necessary ([see testing](#testing-details)) -You can create/regenerate ground-truths for the examples like this: +6) Push and open a draft PR against `main` -```bash -# desktop examples -REGENERATE_SCREENSHOTS=1 pytest -v examples/ -# notebook examples -FASTPLOTLIB_NB_TESTS=1 REGENERATE_SCREENSHOTS=1 pytest --nbmake examples/notebooks/image_widget_test.ipynb -``` -**Please only commit ground-truth images that correspond to your PR** since this will generate ground-truth images for -the entire test suite. diff --git a/docs/source/developer_notes/graphics.rst b/docs/source/developer_notes/graphics.rst new file mode 100644 index 000000000..9efe07b79 --- /dev/null +++ b/docs/source/developer_notes/graphics.rst @@ -0,0 +1,71 @@ +Graphics +======== + + +A ``Graphic`` is something that can be added to a ``PlotArea`` (described in detail in the layouts section). All the various +fastplotlib graphics, such as ``ImageGraphic``, ``ScatterGraphic``, etc. inherit from the ``Graphic`` base class in +``fastplotlib/graphics/_base.py``. It has a few properties that mostly wrap ``pygfx`` ``WorldObject`` properties and transforms. + +.. code-block:: rst + + Graphic + │ + ├─ ImageGraphic + │ + ├─ TextGraphic + │ + ├─ PositionsGraphic + │ │ + │ ├─ LineGraphic + │ │ + │ └─ ScatterGraphic + │ + └─ GraphicCollection + │ + └─ LineCollection + │ + └─ LineStack + +.. + +All graphics can be given a string name for the user's convenience. This allows graphics to be easily accessed from +plots, ex: ``subplot["some_image"]``. + +All graphics contain a ``world_object`` property which is just the ``pygfx.WorldObject`` that this graphic uses. Fastplotlib +keeps a *private* global dictionary of all ``WorldObject`` instances and users are only given a weakref proxy to this world object. +This is due to garbage collection. This may be quite complicated for beginners, for more details see this PR: https://github.com/fastplotlib/fastplotlib/pull/160 . +If you are curious or have more questions on garbage collection in ``fastplotlib`` you're welcome to post an issue :D. + +Graphic collections are groups of graphics. For now, we have a ``LineCollection`` which is a group of ``LineGraphic`` objects. We also have a ``LineStack`` which +inherits from ``LineCollection`` and gives some fixed offset between ``LineGraphic`` objects in the collection. + +Graphic Properties +------------------ + +Graphic properties are all evented, and internally we call these "graphic features". They are the various +aspects of a graphic that the user can change. +The "graphic features" subpackage can be found at ``fastplotlib/graphics/_features``. As we can see this +is a private subpackage and never meant to be accessible to users. + +For example let's look at ``LineGraphic`` in ``fastplotlib/graphics/line.py``. Every graphic has a class variable called +``_features`` which is a set of all graphic properties that are evented. It has the following evented properties: +``"data", "colors", "cmap", "thickness"`` in addition to properties common to all graphics, such as ``"name", "offset", "rotation", and "visible"`` + +Now look at the constructor for the ``LineGraphic`` base class ``PositionsGraphic``, it first creates an instance of ``VertexPositions``. +This is a class that manages vertex positions buffer. It defines the line, and provides additional useful functionality. +For example, every time that the ``data`` is changed, the new data will be marked for upload to the GPU before the next draw. +In addition, event handlers will be called if any event handlers are registered. + +``VertexColors``behaves similarly, but it can perform additional parsing that can create the colors buffer from different +forms of user input. For example if a user runs: ``line_graphic.colors = "blue"``, then ``VertexColors.__setitem__()`` will +create a buffer that corresponds to what ``pygfx.Color`` thinks is "blue". Users can also take advantage of fancy indexing, +ex: ``line_graphics.colors[bool_array] = "red"`` :smile: + +``LineGraphic`` also has a ``VertexCmap``, this manages the line ``VertexColors`` instance to parse colormaps, for example: +``line_graphic.cmap = "jet"`` or even ``line_graphic.cmap[50:] = "viridis"``. + +``LineGraphic`` also has a ``thickness`` property which is pretty simple, and ``DeletedFeature`` which is useful if you need +callbacks to indicate that the graphic has been deleted (for example, removing references to a graphic from a legend). + +Other graphics have properties that are relevant to them, for example ``ImageGraphic`` has ``cmap``, ``vmin``, ``vmax``, +properties unique to images. diff --git a/docs/source/developer_notes/index.rst b/docs/source/developer_notes/index.rst new file mode 100644 index 000000000..1ba4069bd --- /dev/null +++ b/docs/source/developer_notes/index.rst @@ -0,0 +1,39 @@ +Developer Notes +*************** + +Welcome to the Developer Notes for `fastplotlib`. These notes aim to provide detailed and technical information +about the various modules, classes, and functions that make up this library, as well as guidelines on how to write +code that integrates nicely with our package. They are intended to help current and future developers understand +the design decisions, and functioning of the library. + +Intended Audience +----------------- + +These notes are primarily intended for the following groups: + +- **Current Developers**: The Developer Notes can serve as a comprehensive guide to understanding the library, making it easier to debug, modify and maintain the code. + +- **Future Developers**: These notes can help onboard new developers to the project, providing them with detailed explanations of the codebase and its underlying architecture. + +- **Contributors**: If you wish to contribute to the `fastplotlib` project, the Developer Notes can provide a solid foundation of understanding, helping to ensure that your contributions align with the existing structure and design principles of the library. + +- **Advanced Users**: While the primary focus of these notes is on development, they might also be of interest to advanced users who want a deeper understanding of the library's functionality. + +Please note that these notes assume a certain level of programming knowledge. Familiarity with Python, object-oriented programming, and the NumPy and pygfx libraries would be beneficial when reading these notes. + + +Interact with us +---------------- + +If you're considering contributing to the library, first of all, welcome aboard! As a first step, we recommend that you read the [`CONTRIBUTING.md`](https://github.com/fastplotlib/fastplotlib/blob/main/CONTRIBUTING.md) guidelines. +These will help you understand how to interact with other contributors and how to submit your changes. + +If you have any questions or need further clarification on any of the topics covered in these notes, please don't hesitate to reach out to us. You can do so via the [discussion](https://github.com/fastplotlib/fastplotlib/discussions/landing) forum on GitHub. + +We're looking forward to your contributions and to answering any questions you might have! + +.. toctree:: + :maxdepth: 1 + + Graphics + Layouts \ No newline at end of file diff --git a/docs/source/developer_notes/layouts.rst b/docs/source/developer_notes/layouts.rst new file mode 100644 index 000000000..f478ac5c5 --- /dev/null +++ b/docs/source/developer_notes/layouts.rst @@ -0,0 +1,79 @@ +Layouts +======= + +PlotArea +-------- + +This is the main base class within layouts. A ``Figure`` and ``Dock`` are areas within a ``Subplot`` that +inherit from ``PlotArea``. + +``PlotArea`` has the following key properties that allow it to be a "plot area" that can be used to view graphical objects: + +* scene - instance of ``pygfx.Scene`` +* canvas - instance of ``WgpuCanvas`` +* renderer - instance of ``pygfx.WgpuRenderer`` +* viewport - instance of ``pygfx.Viewport`` +* camera - instance of ``pygfx.PerspectiveCamera``, we always just use ``PerspectiveCamera`` and just set ``camera.fov = 0`` for orthographic projections +* controller - instance of ``pygfx.Controller`` + +Abstract method that must be implemented in subclasses: + +* get_rect - must return [x, y, width, height] that defines the viewport rect for this ``PlotArea`` + +Properties specifically used by subplots in a Figure: + +* parent - A parent if relevant, used by individual ``Subplots`` in ``Figure``, and by ``Dock`` which are "docked" subplots at the edges of a subplot. +* position - if a subplot within a ``Figure``, it is the position of this subplot within the ``Figure`` + +Other important properties: + +* graphics - a tuple of weakref proxies to all ``Graphics`` within this ``PlotArea``, users are only given weakref proxies to ``Graphic`` objects, all ``Graphic`` objects are stored in a private global dict. +* selectors - a tuple of weakref proxies to all selectors within this ``PlotArea`` +* legend - a tuple of weakref proxies to all legend graphics within this ``PlotArea`` +* name - plot areas are allowed to have names that the user can use for their convenience + +Important methods: + +* add_graphic - add a ``Graphic`` to the ``PlotArea``, append to the end of the ``PlotArea._graphics`` list +* insert_graphic - insert a ``Graphic`` to the ``PlotArea``, insert to a specific position of the ``PlotArea._graphics`` list +* remove_graphic - remove a graphic from the ``Scene``, **does not delete it** +* delete_graphic - delete a graphic from the ``PlotArea``, performs garbage collection +* clear - deletes all graphics from the ``PlotArea`` +* center_graphic - center camera w.r.t. a ``Graphic`` +* center_scene - center camera w.r.t. entire ``Scene`` +* auto_scale - Auto-scale the camera w.r.t to the ``Scene`` + +In addition, ``PlotArea`` supports ``__getitem__``, so you can do: ``plot_area["graphic_name"]`` to retrieve a ``Graphic`` by +name :smile: + +You can also check if a ``PlotArea`` has certain graphics, ex: ``"some_image_name" in plot_area``, or ``graphic_instance in plot_area`` + +Subplot +------- + +This class inherits from ``PlotArea`` and ``GraphicMethodsMixin``. + +``GraphicMethodsMixin`` is a simple class that just has all the ``add_`` methods. It is autogenerated by a utility script like this: + +.. code-block:: bash + + python scripts/generate_add_methods.py + +Each ``add_`` method basically creates an instance of ``Graphic``, adds it to the ``Subplot``, and returns a weakref +proxy to the ``Graphic``. + +Subplot has one property that is not in ``PlotArea``: + +* docks: a ``dict`` of ``PlotAreas`` which are located at the "top", "right", "left", and "bottom" edges of a ``Subplot``. +By default their size is ``0``. They are useful for putting things like histogram LUT tools. + +The key method in ``Subplot`` is an implementation of ``get_rect`` that returns the viewport rect for this subplot. + +Figure +------ + +Now that we have understood ``PlotArea`` and ``Subplot`` we need a way for the user to create them! + +A ``Figure`` contains a grid of subplot and has methods such as ``show()`` to output the figure. +``Figure.__init__`` basically does a lot of parsing of user arguments to determine how to create +the subplots. All subplots within a ``Figure`` share the same canvas and use different viewports to create the subplots. diff --git a/docs/source/index.rst b/docs/source/index.rst index f855569e3..e22d75af1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,6 +7,7 @@ Welcome to fastplotlib's documentation! Guide GPU Info + Developer Notes FAQ .. toctree::