diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ae7598f41..fdbd87a65 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,6 +10,7 @@ build: - libxcb-xfixes0-dev - mesa-vulkan-drivers - libglfw3 + - pandoc jobs: pre_install: - pip install git+https://github.com/pygfx/pygfx.git@main diff --git a/docs/source/_static/logo.png b/docs/source/_static/logo.png new file mode 100644 index 000000000..304fc88bc --- /dev/null +++ b/docs/source/_static/logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7385f790683edc6c79fb6131e1649bd54d7fefd405cc8b6005ed86ea7dbb8fa6 +size 31759 diff --git a/docs/source/_static/style.css b/docs/source/_static/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/docs/source/_templates/autosummary/class.rst b/docs/source/_templates/autosummary/class.rst new file mode 100644 index 000000000..d4fd5208b --- /dev/null +++ b/docs/source/_templates/autosummary/class.rst @@ -0,0 +1,5 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} diff --git a/docs/source/api/graphic_features.rst b/docs/source/api/graphic_features.rst deleted file mode 100644 index 2fe60ce24..000000000 --- a/docs/source/api/graphic_features.rst +++ /dev/null @@ -1,81 +0,0 @@ -.. _api_graphic_features: - -Graphic Features -**************** - -Image -##### - -.. autoclass:: fastplotlib.graphics.features.ImageDataFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -.. autoclass:: fastplotlib.graphics.features.ImageCmapFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -Heatmap -####### - -.. autoclass:: fastplotlib.graphics.features.HeatmapDataFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -.. autoclass:: fastplotlib.graphics.features.HeatmapCmapFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -Line -#### - -.. autoclass:: fastplotlib.graphics.features.PositionsDataFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -.. autoclass:: fastplotlib.graphics.features.ColorsFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -.. autoclass:: fastplotlib.graphics.features.ThicknessFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -Scatter -####### - -.. autoclass:: fastplotlib.graphics.features.PositionsDataFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -.. autoclass:: fastplotlib.graphics.features.ColorsFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: - -Common -###### - -Features common to all graphics - -.. autoclass:: fastplotlib.graphics.features.PresentFeature - :members: - :inherited-members: - :exclude-members: __init__ - :no-undoc-members: diff --git a/docs/source/api/graphic_features/CmapFeature.rst b/docs/source/api/graphic_features/CmapFeature.rst new file mode 100644 index 000000000..03e3330b7 --- /dev/null +++ b/docs/source/api/graphic_features/CmapFeature.rst @@ -0,0 +1,35 @@ +.. _api.CmapFeature: + +CmapFeature +*********** + +=========== +CmapFeature +=========== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: CmapFeature_api + + CmapFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: CmapFeature_api + + CmapFeature.buffer + CmapFeature.values + +Methods +~~~~~~~ +.. autosummary:: + :toctree: CmapFeature_api + + CmapFeature.add_event_handler + CmapFeature.block_events + CmapFeature.clear_event_handlers + CmapFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/ColorFeature.rst b/docs/source/api/graphic_features/ColorFeature.rst new file mode 100644 index 000000000..3ed84cd70 --- /dev/null +++ b/docs/source/api/graphic_features/ColorFeature.rst @@ -0,0 +1,34 @@ +.. _api.ColorFeature: + +ColorFeature +************ + +============ +ColorFeature +============ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ColorFeature_api + + ColorFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ColorFeature_api + + ColorFeature.buffer + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ColorFeature_api + + ColorFeature.add_event_handler + ColorFeature.block_events + ColorFeature.clear_event_handlers + ColorFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/FeatureEvent.rst b/docs/source/api/graphic_features/FeatureEvent.rst new file mode 100644 index 000000000..f22ee3ef4 --- /dev/null +++ b/docs/source/api/graphic_features/FeatureEvent.rst @@ -0,0 +1,29 @@ +.. _api.FeatureEvent: + +FeatureEvent +************ + +============ +FeatureEvent +============ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: FeatureEvent_api + + FeatureEvent + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: FeatureEvent_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: FeatureEvent_api + + diff --git a/docs/source/api/graphic_features/GraphicFeature.rst b/docs/source/api/graphic_features/GraphicFeature.rst new file mode 100644 index 000000000..7abc3e6b2 --- /dev/null +++ b/docs/source/api/graphic_features/GraphicFeature.rst @@ -0,0 +1,33 @@ +.. _api.GraphicFeature: + +GraphicFeature +************** + +============== +GraphicFeature +============== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeature_api + + GraphicFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeature_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: GraphicFeature_api + + GraphicFeature.add_event_handler + GraphicFeature.block_events + GraphicFeature.clear_event_handlers + GraphicFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/GraphicFeatureIndexable.rst b/docs/source/api/graphic_features/GraphicFeatureIndexable.rst new file mode 100644 index 000000000..7bd1383bc --- /dev/null +++ b/docs/source/api/graphic_features/GraphicFeatureIndexable.rst @@ -0,0 +1,34 @@ +.. _api.GraphicFeatureIndexable: + +GraphicFeatureIndexable +*********************** + +======================= +GraphicFeatureIndexable +======================= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureIndexable_api + + GraphicFeatureIndexable + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureIndexable_api + + GraphicFeatureIndexable.buffer + +Methods +~~~~~~~ +.. autosummary:: + :toctree: GraphicFeatureIndexable_api + + GraphicFeatureIndexable.add_event_handler + GraphicFeatureIndexable.block_events + GraphicFeatureIndexable.clear_event_handlers + GraphicFeatureIndexable.remove_event_handler + diff --git a/docs/source/api/graphic_features/HeatmapCmapFeature.rst b/docs/source/api/graphic_features/HeatmapCmapFeature.rst new file mode 100644 index 000000000..77df37ab0 --- /dev/null +++ b/docs/source/api/graphic_features/HeatmapCmapFeature.rst @@ -0,0 +1,35 @@ +.. _api.HeatmapCmapFeature: + +HeatmapCmapFeature +****************** + +================== +HeatmapCmapFeature +================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapCmapFeature_api + + HeatmapCmapFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapCmapFeature_api + + HeatmapCmapFeature.vmax + HeatmapCmapFeature.vmin + +Methods +~~~~~~~ +.. autosummary:: + :toctree: HeatmapCmapFeature_api + + HeatmapCmapFeature.add_event_handler + HeatmapCmapFeature.block_events + HeatmapCmapFeature.clear_event_handlers + HeatmapCmapFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/HeatmapDataFeature.rst b/docs/source/api/graphic_features/HeatmapDataFeature.rst new file mode 100644 index 000000000..029f0e199 --- /dev/null +++ b/docs/source/api/graphic_features/HeatmapDataFeature.rst @@ -0,0 +1,35 @@ +.. _api.HeatmapDataFeature: + +HeatmapDataFeature +****************** + +================== +HeatmapDataFeature +================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapDataFeature_api + + HeatmapDataFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapDataFeature_api + + HeatmapDataFeature.buffer + +Methods +~~~~~~~ +.. autosummary:: + :toctree: HeatmapDataFeature_api + + HeatmapDataFeature.add_event_handler + HeatmapDataFeature.block_events + HeatmapDataFeature.clear_event_handlers + HeatmapDataFeature.remove_event_handler + HeatmapDataFeature.update_gpu + diff --git a/docs/source/api/graphic_features/ImageCmapFeature.rst b/docs/source/api/graphic_features/ImageCmapFeature.rst new file mode 100644 index 000000000..d2174ff9a --- /dev/null +++ b/docs/source/api/graphic_features/ImageCmapFeature.rst @@ -0,0 +1,35 @@ +.. _api.ImageCmapFeature: + +ImageCmapFeature +**************** + +================ +ImageCmapFeature +================ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmapFeature_api + + ImageCmapFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageCmapFeature_api + + ImageCmapFeature.vmax + ImageCmapFeature.vmin + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageCmapFeature_api + + ImageCmapFeature.add_event_handler + ImageCmapFeature.block_events + ImageCmapFeature.clear_event_handlers + ImageCmapFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/ImageDataFeature.rst b/docs/source/api/graphic_features/ImageDataFeature.rst new file mode 100644 index 000000000..35fe74cf7 --- /dev/null +++ b/docs/source/api/graphic_features/ImageDataFeature.rst @@ -0,0 +1,35 @@ +.. _api.ImageDataFeature: + +ImageDataFeature +**************** + +================ +ImageDataFeature +================ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageDataFeature_api + + ImageDataFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageDataFeature_api + + ImageDataFeature.buffer + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageDataFeature_api + + ImageDataFeature.add_event_handler + ImageDataFeature.block_events + ImageDataFeature.clear_event_handlers + ImageDataFeature.remove_event_handler + ImageDataFeature.update_gpu + diff --git a/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst new file mode 100644 index 000000000..a15825530 --- /dev/null +++ b/docs/source/api/graphic_features/LinearRegionSelectionFeature.rst @@ -0,0 +1,34 @@ +.. _api.LinearRegionSelectionFeature: + +LinearRegionSelectionFeature +**************************** + +============================ +LinearRegionSelectionFeature +============================ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelectionFeature_api + + LinearRegionSelectionFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelectionFeature_api + + LinearRegionSelectionFeature.axis + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelectionFeature_api + + LinearRegionSelectionFeature.add_event_handler + LinearRegionSelectionFeature.block_events + LinearRegionSelectionFeature.clear_event_handlers + LinearRegionSelectionFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/LinearSelectionFeature.rst b/docs/source/api/graphic_features/LinearSelectionFeature.rst new file mode 100644 index 000000000..aeb1ca66b --- /dev/null +++ b/docs/source/api/graphic_features/LinearSelectionFeature.rst @@ -0,0 +1,33 @@ +.. _api.LinearSelectionFeature: + +LinearSelectionFeature +********************** + +====================== +LinearSelectionFeature +====================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LinearSelectionFeature_api + + LinearSelectionFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LinearSelectionFeature_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LinearSelectionFeature_api + + LinearSelectionFeature.add_event_handler + LinearSelectionFeature.block_events + LinearSelectionFeature.clear_event_handlers + LinearSelectionFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/PointsDataFeature.rst b/docs/source/api/graphic_features/PointsDataFeature.rst new file mode 100644 index 000000000..078b1c535 --- /dev/null +++ b/docs/source/api/graphic_features/PointsDataFeature.rst @@ -0,0 +1,34 @@ +.. _api.PointsDataFeature: + +PointsDataFeature +***************** + +================= +PointsDataFeature +================= +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: PointsDataFeature_api + + PointsDataFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: PointsDataFeature_api + + PointsDataFeature.buffer + +Methods +~~~~~~~ +.. autosummary:: + :toctree: PointsDataFeature_api + + PointsDataFeature.add_event_handler + PointsDataFeature.block_events + PointsDataFeature.clear_event_handlers + PointsDataFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/PresentFeature.rst b/docs/source/api/graphic_features/PresentFeature.rst new file mode 100644 index 000000000..1ddbf1ec4 --- /dev/null +++ b/docs/source/api/graphic_features/PresentFeature.rst @@ -0,0 +1,33 @@ +.. _api.PresentFeature: + +PresentFeature +************** + +============== +PresentFeature +============== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: PresentFeature_api + + PresentFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: PresentFeature_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: PresentFeature_api + + PresentFeature.add_event_handler + PresentFeature.block_events + PresentFeature.clear_event_handlers + PresentFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/ThicknessFeature.rst b/docs/source/api/graphic_features/ThicknessFeature.rst new file mode 100644 index 000000000..80219a2cd --- /dev/null +++ b/docs/source/api/graphic_features/ThicknessFeature.rst @@ -0,0 +1,33 @@ +.. _api.ThicknessFeature: + +ThicknessFeature +**************** + +================ +ThicknessFeature +================ +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ThicknessFeature_api + + ThicknessFeature + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ThicknessFeature_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ThicknessFeature_api + + ThicknessFeature.add_event_handler + ThicknessFeature.block_events + ThicknessFeature.clear_event_handlers + ThicknessFeature.remove_event_handler + diff --git a/docs/source/api/graphic_features/index.rst b/docs/source/api/graphic_features/index.rst new file mode 100644 index 000000000..aff2aabda --- /dev/null +++ b/docs/source/api/graphic_features/index.rst @@ -0,0 +1,21 @@ +Graphic Features +**************** + +.. toctree:: + :maxdepth: 1 + + ColorFeature + CmapFeature + ImageCmapFeature + HeatmapCmapFeature + PointsDataFeature + ImageDataFeature + HeatmapDataFeature + PresentFeature + ThicknessFeature + GraphicFeature + GraphicFeatureIndexable + FeatureEvent + to_gpu_supported_dtype + LinearSelectionFeature + LinearRegionSelectionFeature diff --git a/docs/source/api/graphic_features/to_gpu_supported_dtype.rst b/docs/source/api/graphic_features/to_gpu_supported_dtype.rst new file mode 100644 index 000000000..984a76157 --- /dev/null +++ b/docs/source/api/graphic_features/to_gpu_supported_dtype.rst @@ -0,0 +1,29 @@ +.. _api.to_gpu_supported_dtype: + +to_gpu_supported_dtype +********************** + +====================== +to_gpu_supported_dtype +====================== +.. currentmodule:: fastplotlib.graphics._features + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: to_gpu_supported_dtype_api + + to_gpu_supported_dtype + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: to_gpu_supported_dtype_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: to_gpu_supported_dtype_api + + diff --git a/docs/source/api/graphics.rst b/docs/source/api/graphics.rst deleted file mode 100644 index d38045dae..000000000 --- a/docs/source/api/graphics.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _api_graphics: - -Graphics -******** - -Image -##### - -.. autoclass:: fastplotlib.graphics.image.ImageGraphic - :members: - :inherited-members: - -Line -#### - -.. autoclass:: fastplotlib.graphics.line.LineGraphic - :members: - :inherited-members: - -Line Collection -############### - -.. autoclass:: fastplotlib.graphics.line_collection.LineCollection - :members: - :inherited-members: - -Line Stack -########## - -.. autoclass:: fastplotlib.graphics.line_collection.LineStack - :members: - :inherited-members: - -Heatmap -####### - -.. autoclass:: fastplotlib.graphics.image.HeatmapGraphic - :members: - :inherited-members: - -Histogram -######### - -.. autoclass:: fastplotlib.graphics.histogram.HistogramGraphic - :members: - :inherited-members: - -Scatter -####### - -.. autoclass:: fastplotlib.graphics.scatter.ScatterGraphic - :members: - :inherited-members: - -Text -#### - -.. autoclass:: fastplotlib.graphics.text.TextGraphic - :members: - :inherited-members: - diff --git a/docs/source/api/graphics/HeatmapGraphic.rst b/docs/source/api/graphics/HeatmapGraphic.rst new file mode 100644 index 000000000..57466698a --- /dev/null +++ b/docs/source/api/graphics/HeatmapGraphic.rst @@ -0,0 +1,41 @@ +.. _api.HeatmapGraphic: + +HeatmapGraphic +************** + +============== +HeatmapGraphic +============== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapGraphic_api + + HeatmapGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: HeatmapGraphic_api + + HeatmapGraphic.children + HeatmapGraphic.position + HeatmapGraphic.position_x + HeatmapGraphic.position_y + HeatmapGraphic.position_z + HeatmapGraphic.visible + HeatmapGraphic.vmax + HeatmapGraphic.vmin + HeatmapGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: HeatmapGraphic_api + + HeatmapGraphic.add_linear_region_selector + HeatmapGraphic.add_linear_selector + HeatmapGraphic.link + diff --git a/docs/source/api/graphics/HistogramGraphic.rst b/docs/source/api/graphics/HistogramGraphic.rst new file mode 100644 index 000000000..9174092f5 --- /dev/null +++ b/docs/source/api/graphics/HistogramGraphic.rst @@ -0,0 +1,36 @@ +.. _api.HistogramGraphic: + +HistogramGraphic +**************** + +================ +HistogramGraphic +================ +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: HistogramGraphic_api + + HistogramGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: HistogramGraphic_api + + HistogramGraphic.children + HistogramGraphic.position + HistogramGraphic.position_x + HistogramGraphic.position_y + HistogramGraphic.position_z + HistogramGraphic.visible + HistogramGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: HistogramGraphic_api + + diff --git a/docs/source/api/graphics/ImageGraphic.rst b/docs/source/api/graphics/ImageGraphic.rst new file mode 100644 index 000000000..083c72abb --- /dev/null +++ b/docs/source/api/graphics/ImageGraphic.rst @@ -0,0 +1,39 @@ +.. _api.ImageGraphic: + +ImageGraphic +************ + +============ +ImageGraphic +============ +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageGraphic_api + + ImageGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageGraphic_api + + ImageGraphic.children + ImageGraphic.position + ImageGraphic.position_x + ImageGraphic.position_y + ImageGraphic.position_z + ImageGraphic.visible + ImageGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageGraphic_api + + ImageGraphic.add_linear_region_selector + ImageGraphic.add_linear_selector + ImageGraphic.link + diff --git a/docs/source/api/graphics/LineCollection.rst b/docs/source/api/graphics/LineCollection.rst new file mode 100644 index 000000000..003ad2897 --- /dev/null +++ b/docs/source/api/graphics/LineCollection.rst @@ -0,0 +1,44 @@ +.. _api.LineCollection: + +LineCollection +************** + +============== +LineCollection +============== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LineCollection_api + + LineCollection + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LineCollection_api + + LineCollection.children + LineCollection.cmap + LineCollection.cmap_values + LineCollection.graphics + LineCollection.position + LineCollection.position_x + LineCollection.position_y + LineCollection.position_z + LineCollection.visible + LineCollection.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LineCollection_api + + LineCollection.add_graphic + LineCollection.add_linear_region_selector + LineCollection.add_linear_selector + LineCollection.link + LineCollection.remove_graphic + diff --git a/docs/source/api/graphics/LineGraphic.rst b/docs/source/api/graphics/LineGraphic.rst new file mode 100644 index 000000000..75af2c4fe --- /dev/null +++ b/docs/source/api/graphics/LineGraphic.rst @@ -0,0 +1,39 @@ +.. _api.LineGraphic: + +LineGraphic +*********** + +=========== +LineGraphic +=========== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LineGraphic_api + + LineGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LineGraphic_api + + LineGraphic.children + LineGraphic.position + LineGraphic.position_x + LineGraphic.position_y + LineGraphic.position_z + LineGraphic.visible + LineGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LineGraphic_api + + LineGraphic.add_linear_region_selector + LineGraphic.add_linear_selector + LineGraphic.link + diff --git a/docs/source/api/graphics/LineStack.rst b/docs/source/api/graphics/LineStack.rst new file mode 100644 index 000000000..6104d0f74 --- /dev/null +++ b/docs/source/api/graphics/LineStack.rst @@ -0,0 +1,44 @@ +.. _api.LineStack: + +LineStack +********* + +========= +LineStack +========= +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LineStack_api + + LineStack + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LineStack_api + + LineStack.children + LineStack.cmap + LineStack.cmap_values + LineStack.graphics + LineStack.position + LineStack.position_x + LineStack.position_y + LineStack.position_z + LineStack.visible + LineStack.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LineStack_api + + LineStack.add_graphic + LineStack.add_linear_region_selector + LineStack.add_linear_selector + LineStack.link + LineStack.remove_graphic + diff --git a/docs/source/api/graphics/ScatterGraphic.rst b/docs/source/api/graphics/ScatterGraphic.rst new file mode 100644 index 000000000..3c4bf3909 --- /dev/null +++ b/docs/source/api/graphics/ScatterGraphic.rst @@ -0,0 +1,36 @@ +.. _api.ScatterGraphic: + +ScatterGraphic +************** + +============== +ScatterGraphic +============== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ScatterGraphic_api + + ScatterGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ScatterGraphic_api + + ScatterGraphic.children + ScatterGraphic.position + ScatterGraphic.position_x + ScatterGraphic.position_y + ScatterGraphic.position_z + ScatterGraphic.visible + ScatterGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ScatterGraphic_api + + diff --git a/docs/source/api/graphics/TextGraphic.rst b/docs/source/api/graphics/TextGraphic.rst new file mode 100644 index 000000000..c83c108f6 --- /dev/null +++ b/docs/source/api/graphics/TextGraphic.rst @@ -0,0 +1,42 @@ +.. _api.TextGraphic: + +TextGraphic +*********** + +=========== +TextGraphic +=========== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: TextGraphic_api + + TextGraphic + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: TextGraphic_api + + TextGraphic.children + TextGraphic.position + TextGraphic.position_x + TextGraphic.position_y + TextGraphic.position_z + TextGraphic.visible + TextGraphic.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: TextGraphic_api + + TextGraphic.update_face_color + TextGraphic.update_outline_color + TextGraphic.update_outline_size + TextGraphic.update_position + TextGraphic.update_size + TextGraphic.update_text + diff --git a/docs/source/api/graphics/index.rst b/docs/source/api/graphics/index.rst new file mode 100644 index 000000000..fbfa5f6f3 --- /dev/null +++ b/docs/source/api/graphics/index.rst @@ -0,0 +1,14 @@ +Graphics +******** + +.. toctree:: + :maxdepth: 1 + + ImageGraphic + ScatterGraphic + LineGraphic + HistogramGraphic + HeatmapGraphic + LineCollection + LineStack + TextGraphic diff --git a/docs/source/api/gridplot.rst b/docs/source/api/gridplot.rst deleted file mode 100644 index 7e0f877c1..000000000 --- a/docs/source/api/gridplot.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. _api_gridplot: - -GridPlot -######## - -.. autoclass:: fastplotlib.GridPlot - :members: - :inherited-members: diff --git a/docs/source/api/layouts/gridplot.rst b/docs/source/api/layouts/gridplot.rst new file mode 100644 index 000000000..f34d0b8d1 --- /dev/null +++ b/docs/source/api/layouts/gridplot.rst @@ -0,0 +1,98 @@ +.. _api.GridPlot: + +GridPlot +******** + +======== +GridPlot +======== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: GridPlot_api + + GridPlot + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: GridPlot_api + + +Methods +~~~~~~~ +.. autosummary:: + :toctree: GridPlot_api + + GridPlot.add_animations + GridPlot.clear + GridPlot.close + GridPlot.record_start + GridPlot.record_stop + GridPlot.remove_animation + GridPlot.render + GridPlot.show + +======= +Subplot +======= +.. currentmodule:: fastplotlib.layouts._subplot + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Subplot_api + + Subplot + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Subplot_api + + Subplot.camera + Subplot.canvas + Subplot.controller + Subplot.graphics + Subplot.name + Subplot.parent + Subplot.position + Subplot.renderer + Subplot.scene + Subplot.selectors + Subplot.viewport + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Subplot_api + + Subplot.add_animations + Subplot.add_graphic + Subplot.add_heatmap + Subplot.add_histogram + Subplot.add_image + Subplot.add_line + Subplot.add_line_collection + Subplot.add_line_stack + Subplot.add_scatter + Subplot.add_text + Subplot.auto_scale + Subplot.center_graphic + Subplot.center_scene + Subplot.center_title + Subplot.clear + Subplot.delete_graphic + Subplot.get_rect + Subplot.insert_graphic + Subplot.map_screen_to_world + Subplot.remove_animation + Subplot.remove_graphic + Subplot.render + Subplot.set_axes_visibility + Subplot.set_grid_visibility + Subplot.set_title + Subplot.set_viewport_rect + diff --git a/docs/source/api/layouts/plot.rst b/docs/source/api/layouts/plot.rst new file mode 100644 index 000000000..d722bf3c3 --- /dev/null +++ b/docs/source/api/layouts/plot.rst @@ -0,0 +1,70 @@ +.. _api.Plot: + +Plot +**** + +==== +Plot +==== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Plot_api + + Plot + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Plot_api + + Plot.camera + Plot.canvas + Plot.controller + Plot.graphics + Plot.name + Plot.parent + Plot.position + Plot.renderer + Plot.scene + Plot.selectors + Plot.viewport + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Plot_api + + Plot.add_animations + Plot.add_graphic + Plot.add_heatmap + Plot.add_histogram + Plot.add_image + Plot.add_line + Plot.add_line_collection + Plot.add_line_stack + Plot.add_scatter + Plot.add_text + Plot.auto_scale + Plot.center_graphic + Plot.center_scene + Plot.center_title + Plot.clear + Plot.close + Plot.delete_graphic + Plot.get_rect + Plot.insert_graphic + Plot.map_screen_to_world + Plot.record_start + Plot.record_stop + Plot.remove_animation + Plot.remove_graphic + Plot.render + Plot.set_axes_visibility + Plot.set_grid_visibility + Plot.set_title + Plot.set_viewport_rect + Plot.show + diff --git a/docs/source/api/plot.rst b/docs/source/api/plot.rst deleted file mode 100644 index 6b3ecb188..000000000 --- a/docs/source/api/plot.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _api_plot: - -Plot -#### - -.. autoclass:: fastplotlib.Plot - :members: - :inherited-members: - diff --git a/docs/source/api/selectors.rst b/docs/source/api/selectors.rst deleted file mode 100644 index c43f936bd..000000000 --- a/docs/source/api/selectors.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _api_selectors: - -Selectors -********* - -Linear -###### - -.. autoclass:: fastplotlib.graphics.selectors.LinearSelector - :members: - :inherited-members: - -.. autoclass:: fastplotlib.graphics.selectors.LinearRegionSelector - :members: - :inherited-members: diff --git a/docs/source/api/selectors/LinearRegionSelector.rst b/docs/source/api/selectors/LinearRegionSelector.rst new file mode 100644 index 000000000..e1824cfc8 --- /dev/null +++ b/docs/source/api/selectors/LinearRegionSelector.rst @@ -0,0 +1,39 @@ +.. _api.LinearRegionSelector: + +LinearRegionSelector +******************** + +==================== +LinearRegionSelector +==================== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelector_api + + LinearRegionSelector + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelector_api + + LinearRegionSelector.children + LinearRegionSelector.position + LinearRegionSelector.position_x + LinearRegionSelector.position_y + LinearRegionSelector.position_z + LinearRegionSelector.visible + LinearRegionSelector.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LinearRegionSelector_api + + LinearRegionSelector.get_selected_data + LinearRegionSelector.get_selected_index + LinearRegionSelector.get_selected_indices + diff --git a/docs/source/api/selectors/LinearSelector.rst b/docs/source/api/selectors/LinearSelector.rst new file mode 100644 index 000000000..2c30579f1 --- /dev/null +++ b/docs/source/api/selectors/LinearSelector.rst @@ -0,0 +1,40 @@ +.. _api.LinearSelector: + +LinearSelector +************** + +============== +LinearSelector +============== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: LinearSelector_api + + LinearSelector + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: LinearSelector_api + + LinearSelector.children + LinearSelector.position + LinearSelector.position_x + LinearSelector.position_y + LinearSelector.position_z + LinearSelector.visible + LinearSelector.world_object + +Methods +~~~~~~~ +.. autosummary:: + :toctree: LinearSelector_api + + LinearSelector.get_selected_data + LinearSelector.get_selected_index + LinearSelector.get_selected_indices + LinearSelector.make_ipywidget_slider + diff --git a/docs/source/api/selectors/Synchronizer.rst b/docs/source/api/selectors/Synchronizer.rst new file mode 100644 index 000000000..d0fa0c2a8 --- /dev/null +++ b/docs/source/api/selectors/Synchronizer.rst @@ -0,0 +1,32 @@ +.. _api.Synchronizer: + +Synchronizer +************ + +============ +Synchronizer +============ +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: Synchronizer_api + + Synchronizer + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: Synchronizer_api + + Synchronizer.selectors + +Methods +~~~~~~~ +.. autosummary:: + :toctree: Synchronizer_api + + Synchronizer.add + Synchronizer.remove + diff --git a/docs/source/api/selectors/index.rst b/docs/source/api/selectors/index.rst new file mode 100644 index 000000000..918944fd8 --- /dev/null +++ b/docs/source/api/selectors/index.rst @@ -0,0 +1,9 @@ +Selectors +********* + +.. toctree:: + :maxdepth: 1 + + LinearSelector + LinearRegionSelector + Synchronizer diff --git a/docs/source/api/subplot.rst b/docs/source/api/subplot.rst deleted file mode 100644 index b9f7a402b..000000000 --- a/docs/source/api/subplot.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _api_subplot: - -Subplot -####### - -.. note:: ``Subplot`` is NOT meant to be instantiated directly, it only exists as part of a GridPlot. - -.. autoclass:: fastplotlib.layouts._subplot.Subplot - :members: - :inherited-members: diff --git a/docs/source/api/widgets.rst b/docs/source/api/widgets.rst deleted file mode 100644 index c7e621a37..000000000 --- a/docs/source/api/widgets.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _api_widgets: - -Widgets -******* - -ImageWidget -########### - -.. autoclass:: fastplotlib.widgets.image.ImageWidget - :members: - :inherited-members: - diff --git a/docs/source/api/widgets/ImageWidget.rst b/docs/source/api/widgets/ImageWidget.rst new file mode 100644 index 000000000..62ec176ce --- /dev/null +++ b/docs/source/api/widgets/ImageWidget.rst @@ -0,0 +1,41 @@ +.. _api.ImageWidget: + +ImageWidget +*********** + +=========== +ImageWidget +=========== +.. currentmodule:: fastplotlib + +Constructor +~~~~~~~~~~~ +.. autosummary:: + :toctree: ImageWidget_api + + ImageWidget + +Properties +~~~~~~~~~~ +.. autosummary:: + :toctree: ImageWidget_api + + ImageWidget.current_index + ImageWidget.data + ImageWidget.dims_order + ImageWidget.gridplot + ImageWidget.managed_graphics + ImageWidget.ndim + ImageWidget.slider_dims + ImageWidget.sliders + ImageWidget.window_funcs + +Methods +~~~~~~~ +.. autosummary:: + :toctree: ImageWidget_api + + ImageWidget.reset_vmin_vmax + ImageWidget.set_data + ImageWidget.show + diff --git a/docs/source/api/widgets/index.rst b/docs/source/api/widgets/index.rst new file mode 100644 index 000000000..5cb5299f6 --- /dev/null +++ b/docs/source/api/widgets/index.rst @@ -0,0 +1,7 @@ +Widgets +******* + +.. toctree:: + :maxdepth: 1 + + ImageWidget diff --git a/docs/source/conf.py b/docs/source/conf.py index 7be450060..d65ea7193 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -2,21 +2,31 @@ # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html -from bs4 import BeautifulSoup -from typing import * +import fastplotlib # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = 'fastplotlib' -copyright = '2022, Kushal Kolar, Caitlin Lewis' +copyright = '2023, Kushal Kolar, Caitlin Lewis' author = 'Kushal Kolar, Caitlin Lewis' -release = 'v0.1.0.a6' +release = fastplotlib.__version__ # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -extensions = ["sphinx.ext.napoleon", "sphinx.ext.autodoc"] +extensions = [ + "sphinx.ext.napoleon", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx_copybutton", + "sphinx_design", + "nbsphinx", +] + +autosummary_generate = True templates_path = ['_templates'] exclude_patterns = [] @@ -26,10 +36,11 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'pydata_sphinx_theme' -html_theme_options = {"page_sidebar_items": ["class_page_toc"]} +html_theme = "furo" html_static_path = ['_static'] +html_logo = "_static/logo.png" +html_title = f"v{release}" autodoc_member_order = 'groupwise' autoclass_content = "both" @@ -37,47 +48,14 @@ autodoc_typehints = "description" autodoc_typehints_description_target = "documented_params" -def _setup_navbar_side_toctree(app: Any): - - def add_class_toctree_function(app: Any, pagename: Any, templatename: Any, context: Any, doctree: Any): - def get_class_toc() -> Any: - soup = BeautifulSoup(context["body"], "html.parser") - - matches = soup.find_all('dl') - if matches is None or len(matches) == 0: - return "" - items = [] - deeper_depth = matches[0].find('dt').get('id').count(".") - for match in matches: - match_dt = match.find('dt') - if match_dt is not None and match_dt.get('id') is not None: - current_title = match_dt.get('id') - current_depth = match_dt.get('id').count(".") - current_link = match.find(class_="headerlink") - if current_link is not None: - if deeper_depth > current_depth: - deeper_depth = current_depth - if deeper_depth == current_depth: - items.append({ - "title": current_title.split('.')[-1], - "link": current_link["href"], - "attributes_and_methods": [] - }) - if deeper_depth < current_depth: - items[-1]["attributes_and_methods"].append( - { - "title": current_title.split('.')[-1], - "link": current_link["href"], - } - ) - return items - context["get_class_toc"] = get_class_toc - - app.connect("html-page-context", add_class_toctree_function) - - -def setup(app: Any): - for setup_function in [ - _setup_navbar_side_toctree, - ]: - setup_function(app) +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'numpy': ('https://numpy.org/doc/stable/', None), + 'pygfx': ('https://pygfx.readthedocs.io/en/latest', None) +} + +html_theme_options = { + "source_repository": "https://github.com/kushalkolar/fastplotlib", + "source_branch": "master", + "source_directory": "docs/", +} diff --git a/docs/source/fastplotlib_banner.xcf b/docs/source/fastplotlib_banner.xcf new file mode 100644 index 000000000..e632ed0d7 Binary files /dev/null and b/docs/source/fastplotlib_banner.xcf differ diff --git a/docs/source/fastplotlib_logo.xcf b/docs/source/fastplotlib_logo.xcf new file mode 100644 index 000000000..f80b3e1b5 Binary files /dev/null and b/docs/source/fastplotlib_logo.xcf differ diff --git a/docs/source/generate_rst.py b/docs/source/generate_rst.py new file mode 100644 index 000000000..3f9c7ac83 --- /dev/null +++ b/docs/source/generate_rst.py @@ -0,0 +1,266 @@ +from typing import * +import inspect +from pathlib import Path +import os + +import fastplotlib +from fastplotlib.layouts._subplot import Subplot +from fastplotlib import graphics +from fastplotlib.graphics import _features, selectors +from fastplotlib import widgets + +current_dir = Path(__file__).parent.resolve() + +API_DIR = current_dir.joinpath("api") +LAYOUTS_DIR = API_DIR.joinpath("layouts") +GRAPHICS_DIR = API_DIR.joinpath("graphics") +GRAPHIC_FEATURES_DIR = API_DIR.joinpath("graphic_features") +SELECTORS_DIR = API_DIR.joinpath("selectors") +WIDGETS_DIR = API_DIR.joinpath("widgets") + +doc_sources = [ + API_DIR, + LAYOUTS_DIR, + GRAPHICS_DIR, + GRAPHIC_FEATURES_DIR, + SELECTORS_DIR, + WIDGETS_DIR +] + +for source_dir in doc_sources: + os.makedirs(source_dir, exist_ok=True) + + +def get_public_members(cls) -> Tuple[List[str], List[str]]: + """ + Returns (public_methods, public_properties) + + Parameters + ---------- + cls + + Returns + ------- + + """ + methods = list() + properties = list() + for member in inspect.getmembers(cls): + # only document public methods + if member[0].startswith("_"): + continue + + if callable(member[1]): + methods.append(member[0]) + elif isinstance(member[1], property): + properties.append(member[0]) + + return methods, properties + + +def generate_class( + cls: type, + module: str, +): + name = cls.__name__ + methods, properties = get_public_members(cls) + methods = [ + f"{name}.{m}" for m in methods + ] + + properties = [ + f"{name}.{p}" for p in properties + ] + + underline = "=" * len(name) + + methods_str = "\n ".join([""] + methods) + properties_str = "\n ".join([""] + properties) + + out = ( + f"{underline}\n" + f"{name}\n" + f"{underline}\n" + f".. currentmodule:: {module}\n" + f"\n" + f"Constructor\n" + f"~~~~~~~~~~~\n" + f".. autosummary::\n" + f" :toctree: {name}_api\n" + f"\n" + f" {name}\n" + f"\n" + f"Properties\n" + f"~~~~~~~~~~\n" + f".. autosummary::\n" + f" :toctree: {name}_api\n" + f"{properties_str}\n" + f"\n" + f"Methods\n" + f"~~~~~~~\n" + f".. autosummary::\n" + f" :toctree: {name}_api\n" + f"{methods_str}\n" + f"\n" + ) + + return out + + +def generate_page( + page_name: str, + modules: List[str], + classes: List[type], + source_path: Path, +): + page_name_underline = "*" * len(page_name) + with open(source_path, "w") as f: + f.write( + f".. _api.{page_name}:\n" + f"\n" + f"{page_name}\n" + f"{page_name_underline}\n" + f"\n" + ) + + for cls, module in zip(classes, modules): + to_write = generate_class(cls, module) + f.write(to_write) + + +def main(): + generate_page( + page_name="Plot", + classes=[fastplotlib.Plot], + modules=["fastplotlib"], + source_path=LAYOUTS_DIR.joinpath("plot.rst") + ) + + generate_page( + page_name="GridPlot", + classes=[fastplotlib.GridPlot, Subplot], + modules=["fastplotlib", "fastplotlib.layouts._subplot"], + source_path=LAYOUTS_DIR.joinpath("gridplot.rst") + ) + + # the rest of this is a mess and can be refactored later + + graphic_classes = [ + getattr(graphics, g) for g in graphics.__all__ + ] + + graphic_class_names = [ + g.__name__ for g in graphic_classes + ] + + graphic_class_names_str = "\n ".join([""] + graphic_class_names) + + # graphic classes index file + with open(GRAPHICS_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Graphics\n" + f"********\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{graphic_class_names_str}\n" + ) + + for graphic_cls in graphic_classes: + generate_page( + page_name=graphic_cls.__name__, + classes=[graphic_cls], + modules=["fastplotlib"], + source_path=GRAPHICS_DIR.joinpath(f"{graphic_cls.__name__}.rst") + ) + ############################################################################## + + feature_classes = [ + getattr(_features, f) for f in _features.__all__ + ] + + feature_class_names = [ + f.__name__ for f in feature_classes + ] + + feature_class_names_str = "\n ".join([""] + feature_class_names) + + with open(GRAPHIC_FEATURES_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Graphic Features\n" + f"****************\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{feature_class_names_str}\n" + ) + + for feature_cls in feature_classes: + generate_page( + page_name=feature_cls.__name__, + classes=[feature_cls], + modules=["fastplotlib.graphics._features"], + source_path=GRAPHIC_FEATURES_DIR.joinpath(f"{feature_cls.__name__}.rst") + ) + ############################################################################## + + selector_classes = [ + getattr(selectors, s) for s in selectors.__all__ + ] + + selector_class_names = [ + s.__name__ for s in selector_classes + ] + + selector_class_names_str = "\n ".join([""] + selector_class_names) + + with open(SELECTORS_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Selectors\n" + f"*********\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{selector_class_names_str}\n" + ) + + for selector_cls in selector_classes: + generate_page( + page_name=selector_cls.__name__, + classes=[selector_cls], + modules=["fastplotlib"], + source_path=SELECTORS_DIR.joinpath(f"{selector_cls.__name__}.rst") + ) + ############################################################################## + + widget_classes = [ + getattr(widgets, w) for w in widgets.__all__ + ] + + widget_class_names = [ + w.__name__ for w in widget_classes + ] + + widget_class_names_str = "\n ".join([""] + widget_class_names) + + with open(WIDGETS_DIR.joinpath("index.rst"), "w") as f: + f.write( + f"Widgets\n" + f"*******\n" + f"\n" + f".. toctree::\n" + f" :maxdepth: 1\n" + f"{widget_class_names_str}\n" + ) + + for widget_cls in widget_classes: + generate_page( + page_name=widget_cls.__name__, + classes=[widget_cls], + modules=["fastplotlib"], + source_path=WIDGETS_DIR.joinpath(f"{widget_cls.__name__}.rst") + ) + + +if __name__ == "__main__": + main() diff --git a/docs/source/index.rst b/docs/source/index.rst index 9a0723af7..99f342fb9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,34 +6,43 @@ Welcome to fastplotlib's documentation! ======================================= +.. toctree:: + :caption: Quick Start + :maxdepth: 2 + + quickstart + .. toctree:: :maxdepth: 1 - :caption: Contents: + :caption: API - Plot - Subplot - Gridplot - Graphics - Graphic Features - Selectors - Widgets + Plot + Gridplot + Graphics + Graphic Features + Selectors + Widgets Summary ======= -``fastplotlib`` is a fast plotting library built using ``pygfx`` render engine utilizing `Vulkan `_ via WGPU. We are focused on fast interactive plotting in the notebook using an expressive API. It also works within desktop application using ``glfw`` or ``Qt``. +A fast plotting library built using the `pygfx `_ render engine utilizing `Vulkan `_, `DX12 `_, or `Metal `_ via `WGPU `_, so it is very fast! We also aim to be an expressive plotting library that enables rapid prototyping for large scale explorative scientific visualization. `fastplotlib` will run on any framework that ``pygfx`` runs on, this includes ``glfw``, ``Qt`` and ``jupyter lab`` + + Installation ============ -For installation please see the instruction on the README on GitHub: +For installation please see the instructions on GitHub: -https://github.com/kushalkolar/fastplotlib +https://github.com/kushalkolar/fastplotlib#installation Contributing ============ -We're open to contributions! If you think there is any useful functionality that can be added, post an issue on the repo with your idea. Also, take a look at the `Roadmap 2023 `_ for future plans or ways in which you could contribute. +Contributions are welcome! See the contributing guide on GitHub: https://github.com/kushalkolar/fastplotlib/blob/master/CONTRIBUTING.md. + +Also take a look at the `Roadmap 2023 `_ for future plans or ways in which you could contribute. Indices and tables ================== diff --git a/docs/source/quickstart.ipynb b/docs/source/quickstart.ipynb new file mode 100644 index 000000000..66543c063 --- /dev/null +++ b/docs/source/quickstart.ipynb @@ -0,0 +1,1431 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "93740a09-9111-4777-ad57-173e9b80a2f0", + "metadata": { + "tags": [] + }, + "source": [ + "# Quick Start Guide 🚀\n", + "\n", + "This notebook goes the basic components of the `fastplotlib` API, image, image updates, line plots, and scatter plots.\n", + "\n", + "**NOTE: This quick start guide in the docs is NOT interactive. Download the examples from the repo and try them on your own computer. You can run the desktop examples directly if you have `glfw` installed, or try the notebook demos:** https://github.com/kushalkolar/fastplotlib/tree/master/examples\n", + "\n", + "It will not be possible to have live demos on the docs until someone can figure out how to get [pygfx](https://github.com/pygfx/pygfx) to work with `wgpu` in the browser, perhaps through [pyodide](https://github.com/pyodide/pyodide) or something :D." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb57c3d3-f20d-4d88-9e7a-04b9309bc637", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import fastplotlib as fpl\n", + "from ipywidgets import VBox, HBox, IntSlider\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d663646-3d3b-4f5b-a083-a5daca65cb4f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os" + ] + }, + { + "cell_type": "markdown", + "id": "a9b386ac-9218-4f8f-97b3-f29b4201ef55", + "metadata": {}, + "source": [ + "## Images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "237823b7-e2c0-4e2f-9ee8-e3fc2b4453c4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# create a `Plot` instance\n", + "plot = fpl.Plot()\n", + "\n", + "# make some random 2D image data\n", + "data = np.random.rand(512, 512)\n", + "\n", + "# plot the image data\n", + "image_graphic = plot.add_image(data=data, name=\"random-image\")\n", + "\n", + "# show the plot\n", + "plot.show()" + ] + }, + { + "cell_type": "markdown", + "id": "be5b408f-dd91-4e36-807a-8c22c8d7d216", + "metadata": {}, + "source": [ + "In live notebooks or desktop applications, you can use the handle on the bottom right corner of the _canvas_ to resize it. You can also pan and zoom using your mouse!" + ] + }, + { + "cell_type": "markdown", + "id": "7c3b637c-a26b-416e-936c-705275852a8a", + "metadata": {}, + "source": [ + "Changing graphic \"features\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de816c88-1c4a-4071-8a5e-c46c93671ef5", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic.cmap = \"viridis\"" + ] + }, + { + "cell_type": "markdown", + "id": "ecc72f23-22ea-4bd1-b9a0-fd4e14baa79f", + "metadata": {}, + "source": [ + "This is how you can take a snapshot of the canvas. Snapshots are shown throughout this doc page for the purposes of documentation, they are NOT necessary for real interactive usage. Download the notebooks to run live demos." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebc87904-f705-46f0-8f94-fc3b1c6c8e30", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "de0653cf-937e-4d0f-965d-296fccaac53e", + "metadata": {}, + "source": [ + "Setting image data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09350854-5058-4574-a01d-84d00e276c57", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic.data = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9bcc3943-ea40-4905-a2a2-29e2620f00c8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "05034a44-f207-45a0-9b5e-3ba7cc118107", + "metadata": {}, + "source": [ + "Setting image data with slicing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83b2db1b-2783-4e89-bcf3-66bb6e09e18a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic.data[::15, :] = 1\n", + "image_graphic.data[:, ::15] = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d400b00b-bdf0-4383-974f-9cccd4cd48b6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "4abfe97e-8aa6-42c0-8b23-797153a885e3", + "metadata": {}, + "source": [ + "Setting image data back to random" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e298c1c-7551-4401-ade0-b9af7d2bbe23", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic.data = np.random.rand(512, 512)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49d44536-b36c-47be-9c09-46a81a2c8607", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "67b92ffd-40cc-43fe-9df9-0e0d94763d8e", + "metadata": {}, + "source": [ + "Plots are indexable and give you their graphics by name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6ba689c-ff4a-44ef-9663-f2c8755072c4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot.graphics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b18f4e3-e13b-46d5-af1f-285c5a7fdc12", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot[\"random-image\"]" + ] + }, + { + "cell_type": "markdown", + "id": "4316a8b5-5f33-427a-8f52-b101d1daab67", + "metadata": {}, + "source": [ + "The `Graphic` instance is also returned when you call `plot.add_`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b5c1321-1fd4-44bc-9433-7439ad3e22cf", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b12bf75e-4e93-4930-9146-e96324fdf3f6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "image_graphic == plot[\"random-image\"]" + ] + }, + { + "cell_type": "markdown", + "id": "1cb03f42-1029-4b16-a16b-35447d9e2955", + "metadata": { + "tags": [] + }, + "source": [ + "## Image updates\n", + "\n", + "This examples show how you can define animation functions that run on every render cycle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aadd757f-6379-4f52-a709-46aa57c56216", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# create another `Plot` instance\n", + "plot_v = fpl.Plot()\n", + "\n", + "plot.canvas.max_buffered_frames = 1\n", + "\n", + "# make some random data again\n", + "data = np.random.rand(512, 512)\n", + "\n", + "# plot the data\n", + "plot_v.add_image(data=data, name=\"random-image\")\n", + "\n", + "# a function to update the image_graphic\n", + "# a plot will pass its plot instance to the animation function as an arugment\n", + "def update_data(plot_instance):\n", + " new_data = np.random.rand(512, 512)\n", + " plot_instance[\"random-image\"].data = new_data\n", + "\n", + "#add this as an animation function\n", + "plot_v.add_animations(update_data)\n", + "\n", + "# show the plot\n", + "plot_v.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b313eda1-6e6c-466f-9fd5-8b70c1d3c110", + "metadata": {}, + "source": [ + "**Share controllers across plots**\n", + "\n", + "This example creates a new plot, but it synchronizes the pan-zoom controller" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86e70b1e-4328-4035-b992-70dff16d2a69", + "metadata": {}, + "outputs": [], + "source": [ + "plot_sync = fpl.Plot(controller=plot_v.controller)\n", + "\n", + "data = np.random.rand(512, 512)\n", + "\n", + "image_graphic_instance = plot_sync.add_image(data=data, cmap=\"viridis\")\n", + "\n", + "# you will need to define a new animation function for this graphic\n", + "def update_data_2():\n", + " new_data = np.random.rand(512, 512)\n", + " # alternatively, you can use the stored reference to the graphic as well instead of indexing the Plot\n", + " image_graphic_instance.data = new_data\n", + "\n", + "plot_sync.add_animations(update_data_2)\n", + "\n", + "plot_sync.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f226c9c2-8d0e-41ab-9ab9-1ae31fd91de5", + "metadata": {}, + "source": [ + "Keeping a reference to the Graphic instance, as shown above `image_graphic_instance`, is useful if you're creating something where you need flexibility in the naming of the graphics" + ] + }, + { + "cell_type": "markdown", + "id": "d11fabb7-7c76-4e94-893d-80ed9ee3be3d", + "metadata": {}, + "source": [ + "You can also use `ipywidgets.VBox` and `HBox` to stack plots. See the `gridplot` notebooks for a proper gridplot interface for more automated subplotting\n", + "\n", + "Not shown in the docs, try the live demo for this feature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef9743b3-5f81-4b79-9502-fa5fca08e56d", + "metadata": {}, + "outputs": [], + "source": [ + "#VBox([plot_v.canvas, plot_sync.show()])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11839d95-8ff7-444c-ae13-6b072c3112c5", + "metadata": {}, + "outputs": [], + "source": [ + "#HBox([plot_v.show(), plot_sync.show()])" + ] + }, + { + "cell_type": "markdown", + "id": "e7859338-8162-408b-ac72-37e606057045", + "metadata": { + "tags": [] + }, + "source": [ + "## Line plots\n", + "\n", + "2D line plots\n", + "\n", + "This example plots a sine wave, cosine wave, and ricker wavelet and demonstrates how **Graphic Features** can be modified by slicing!" + ] + }, + { + "cell_type": "markdown", + "id": "a6fee1c2-4a24-4325-bca2-26e5a4bf6338", + "metadata": {}, + "source": [ + "Generate some data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e8280da-b421-43a5-a1a6-2a196a408e9a", + "metadata": {}, + "outputs": [], + "source": [ + "# linspace, create 100 evenly spaced x values from -10 to 10\n", + "xs = np.linspace(-10, 10, 100)\n", + "# sine wave\n", + "ys = np.sin(xs)\n", + "sine = np.dstack([xs, ys])[0]\n", + "\n", + "# cosine wave\n", + "ys = np.cos(xs) + 5\n", + "cosine = np.dstack([xs, ys])[0]\n", + "\n", + "# sinc function\n", + "a = 0.5\n", + "ys = np.sinc(xs) * 3 + 8\n", + "sinc = np.dstack([xs, ys])[0]" + ] + }, + { + "cell_type": "markdown", + "id": "fbb806e5-1565-4189-936c-b7cf147a59ee", + "metadata": {}, + "source": [ + "Plot all of it on the same plot. Each line plot will be an individual Graphic, you can have any combination of graphics on a plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93a5d1e6-d019-4dd0-a0d1-25d1704ab7a7", + "metadata": {}, + "outputs": [], + "source": [ + "# Create a plot instance\n", + "plot_l = fpl.Plot()\n", + "\n", + "# plot sine wave, use a single color\n", + "sine_graphic = plot_l.add_line(data=sine, thickness=5, colors=\"magenta\")\n", + "\n", + "# you can also use colormaps for lines!\n", + "cosine_graphic = plot_l.add_line(data=cosine, thickness=12, cmap=\"autumn\")\n", + "\n", + "# or a list of colors for each datapoint\n", + "colors = [\"r\"] * 25 + [\"purple\"] * 25 + [\"y\"] * 25 + [\"b\"] * 25\n", + "sinc_graphic = plot_l.add_line(data=sinc, thickness=5, colors = colors)\n", + "\n", + "plot_l.show()" + ] + }, + { + "cell_type": "markdown", + "id": "22dde600-0f56-4370-b017-c8f23a6c01aa", + "metadata": {}, + "source": [ + "\"stretching\" the camera, useful for large timeseries data\n", + "\n", + "Set `maintain_aspect = False` on a camera, and then use the right mouse button and move the mouse to stretch and squeeze the view!\n", + "\n", + "You can also click the **`1:1`** button to toggle this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2695f023-f6ce-4e26-8f96-4fbed5510d1d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.camera.maintain_aspect = False" + ] + }, + { + "cell_type": "markdown", + "id": "1651e965-f750-47ac-bf53-c23dae84cc98", + "metadata": {}, + "source": [ + "reset the plot area" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba50a6ed-0f1b-4795-91dd-a7c3e40b8e3c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.auto_scale(maintain_aspect=True)" + ] + }, + { + "cell_type": "markdown", + "id": "dcd68796-c190-4c3f-8519-d73b98ff6367", + "metadata": {}, + "source": [ + "Graphic features support slicing! :D " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb0d13ed-ef07-46ff-b19e-eeca4c831037", + "metadata": {}, + "outputs": [], + "source": [ + "# indexing of colors\n", + "cosine_graphic.colors[:15] = \"magenta\"\n", + "cosine_graphic.colors[90:] = \"red\"\n", + "cosine_graphic.colors[60] = \"w\"\n", + "\n", + "# indexing to assign colormaps to entire lines or segments\n", + "sinc_graphic.cmap[10:50] = \"gray\"\n", + "sine_graphic.cmap = \"seismic\"\n", + "\n", + "# more complex indexing, set the blue value directly from an array\n", + "cosine_graphic.colors[65:90, 0] = np.linspace(0, 1, 90-65)" + ] + }, + { + "cell_type": "markdown", + "id": "bfe14ed3-e81f-4058-96a7-e2720b6d2f45", + "metadata": {}, + "source": [ + "Make a snapshot of the canvas after slicing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a061a888-d732-406e-a9c2-8cc632fbc368", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "c9689887-cdf3-4a4d-948f-7efdb09bde4e", + "metadata": {}, + "source": [ + "**You can capture changes to a graphic feature as events**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cfa001f6-c640-4f91-beb0-c19b030e503f", + "metadata": {}, + "outputs": [], + "source": [ + "def callback_func(event_data):\n", + " print(event_data)\n", + "\n", + "# Will print event data when the color changes\n", + "cosine_graphic.colors.add_event_handler(callback_func)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb8a0f95-0063-4cd4-a117-e3d62c6e120d", + "metadata": {}, + "outputs": [], + "source": [ + "# more complex indexing of colors\n", + "# from point 15 - 30, set every 3rd point as \"cyan\"\n", + "cosine_graphic.colors[15:50:3] = \"cyan\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3da9a43b-35bd-4b56-9cc7-967536aac967", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "c29f81f9-601b-49f4-b20c-575c56e58026", + "metadata": {}, + "source": [ + "Graphic `data` is also indexable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1a4314b-5723-43c7-94a0-b4cbb0e44d60", + "metadata": {}, + "outputs": [], + "source": [ + "cosine_graphic.data[10:50:5, :2] = sine[10:50:5]\n", + "cosine_graphic.data[90:, 1] = 7" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "682db47b-8c7a-4934-9be4-2067e9fb12d5", + "metadata": {}, + "outputs": [], + "source": [ + "cosine_graphic.data[0] = np.array([[-10, 0, 0]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f779cba0-7ee2-4795-8da8-9a9593d3893e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "3f6d264b-1b03-407e-9d83-cd6cfb02e706", + "metadata": {}, + "source": [ + "Toggle the presence of a graphic within the scene" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcba75b7-9a1e-4aae-9dec-715f7f7456c3", + "metadata": {}, + "outputs": [], + "source": [ + "sinc_graphic.present = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5e22d0f-a244-47e2-9a2d-1eaf79eda1d9", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "763b9943-53a4-4e2a-b47a-4e9e5c9d7be3", + "metadata": {}, + "outputs": [], + "source": [ + "sinc_graphic.present = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b22a8660-26b3-4c73-b87a-df9d7cb4353a", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "86f4e535-ce88-415a-b8d2-53612a2de7b9", + "metadata": {}, + "source": [ + "You can create callbacks to `present` too, for example to re-scale the plot w.r.t. graphics that are present in the scene" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64a20a16-75a5-4772-a849-630ade9be4ff", + "metadata": {}, + "outputs": [], + "source": [ + "sinc_graphic.present.add_event_handler(plot_l.auto_scale)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb093046-c94c-4085-86b4-8cd85cb638ff", + "metadata": {}, + "outputs": [], + "source": [ + "sinc_graphic.present = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9dd6a54-3460-4fb7-bffb-82fd9288902f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f05981c3-c768-4631-ae62-6a8407b20c4c", + "metadata": {}, + "outputs": [], + "source": [ + "sinc_graphic.present = True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb5bf73e-b015-4b4f-82a0-c3ae8cc39ef7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "05f93e93-283b-45d8-ab31-8d15a7671dd2", + "metadata": {}, + "source": [ + "You can set the z-positions of graphics to have them appear under or over other graphics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6bb33406-5bef-455b-86ea-358a7d3ffa94", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "img = np.random.rand(20, 100)\n", + "\n", + "plot_l.add_image(img, name=\"image\", cmap=\"gray\")\n", + "\n", + "# z axix position -1 so it is below all the lines\n", + "plot_l[\"image\"].position_z = -1\n", + "plot_l[\"image\"].position_x = -50" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b586a89-ca3e-4e88-a801-bdd665384f59", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "2c90862e-2f2a-451f-a468-0cf6b857e87a", + "metadata": {}, + "source": [ + "### 3D line plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c51229f-13a2-4653-bff3-15d43ddbca7b", + "metadata": {}, + "outputs": [], + "source": [ + "# just set the camera as \"3d\", the rest is basically the same :D \n", + "plot_l3d = fpl.Plot(camera='3d')\n", + "\n", + "# create a spiral\n", + "phi = np.linspace(0, 30, 200)\n", + "\n", + "xs = phi * np.cos(phi)\n", + "ys = phi * np.sin(phi)\n", + "zs = phi\n", + "\n", + "# use 3D data\n", + "# note: you usually mix 3D and 2D graphics on the same plot\n", + "spiral = np.dstack([xs, ys, zs])[0]\n", + "\n", + "plot_l3d.add_line(data=spiral, thickness=2, cmap='winter')\n", + "\n", + "plot_l3d.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28eb7014-4773-4a34-8bfc-bd3a46429012", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_l3d.auto_scale(maintain_aspect=True)" + ] + }, + { + "cell_type": "markdown", + "id": "a202b3d0-2a0b-450a-93d4-76d0a1129d1d", + "metadata": {}, + "source": [ + "## Scatter plots\n", + "\n", + "Plot tens of thousands or millions of points\n", + "\n", + "There might be a small delay for a few seconds before the plot shows, this is due to shaders being compiled and a few other things. The plot should be very fast and responsive once it is displayed and future modifications should also be fast!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39252df5-9ae5-4132-b97b-2785c5fa92ea", + "metadata": {}, + "outputs": [], + "source": [ + "# create a random distribution\n", + "# only 1,000 points shown here in the docs, but it can be millions\n", + "n_points = 1_000\n", + "\n", + "# if you have a good GPU go for 1.5 million points :D \n", + "# this is multiplied by 3\n", + "#n_points = 500_000\n", + "\n", + "# dimensions always have to be [n_points, xyz]\n", + "dims = (n_points, 3)\n", + "\n", + "clouds_offset = 15\n", + "\n", + "# create some random clouds\n", + "normal = np.random.normal(size=dims, scale=5)\n", + "# stack the data into a single array\n", + "cloud = np.vstack(\n", + " [\n", + " normal - clouds_offset,\n", + " normal,\n", + " normal + clouds_offset,\n", + " ]\n", + ")\n", + "\n", + "# color each of them separately\n", + "colors = [\"yellow\"] * n_points + [\"cyan\"] * n_points + [\"magenta\"] * n_points\n", + "\n", + "# create plot\n", + "plot_s = fpl.Plot()\n", + "\n", + "# use an alpha value since this will be a lot of points\n", + "scatter_graphic = plot_s.add_scatter(data=cloud, sizes=3, colors=colors, alpha=0.7)\n", + "\n", + "plot_s.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b6e4a704-ee6b-4316-956e-acb4dcc1c6f2", + "metadata": {}, + "source": [ + "**Scatter graphic features work similarly to line graphic**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fa46ec0-8680-44f5-894c-559de3145932", + "metadata": {}, + "outputs": [], + "source": [ + "# half of the first cloud's points to red\n", + "scatter_graphic.colors[:n_points:2] = \"r\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "293a4793-44b9-4d18-ae6a-68e7c6f91acc", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_s.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e4dc71e4-5144-436f-a464-f2a29eee8f0b", + "metadata": {}, + "outputs": [], + "source": [ + "# set the green value directly\n", + "scatter_graphic.colors[n_points:n_points * 2, 1] = 0.3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ea7852d-fdae-401b-83b6-b6cfd975f64f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_s.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b637a29-cd5e-4011-ab81-3f91490d9ecd", + "metadata": {}, + "outputs": [], + "source": [ + "# set color values directly using an array\n", + "scatter_graphic.colors[n_points * 2:] = np.repeat([[1, 1, 0, 0.5]], n_points, axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02c19f51-6436-4601-976e-04326df0de81", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_s.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4084fce-78a2-48b3-9a0d-7b57c165c3c1", + "metadata": {}, + "outputs": [], + "source": [ + "# change the data, change y-values\n", + "scatter_graphic.data[n_points:n_points * 2, 1] += 15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ec43f58-4710-4603-9358-682c4af3f701", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_s.canvas.snapshot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f486083e-7c58-4255-ae1a-3fe5d9bfaeed", + "metadata": {}, + "outputs": [], + "source": [ + "# set x values directly but using an array\n", + "scatter_graphic.data[n_points:n_points * 2, 0] = np.linspace(-40, 0, n_points)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6bcb3bc3-4b75-4bbc-b8ca-f8a3219ec3d7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "plot_s.canvas.snapshot()" + ] + }, + { + "cell_type": "markdown", + "id": "d9e554de-c436-4684-a46a-ce8a33d409ac", + "metadata": {}, + "source": [ + "## ipywidget layouts\n", + "\n", + "This just plots everything from these examples in a single output cell" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01a6f70b-c81b-4ee5-8a6b-d979b87227eb", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# row1 = HBox([plot.show(), plot_v.show(), plot_sync.show()])\n", + "# row2 = HBox([plot_l.show(), plot_l3d.show(), plot_s.show()])\n", + "\n", + "# VBox([row1, row2])" + ] + }, + { + "cell_type": "markdown", + "id": "a26c0063-b7e0-4f36-bb14-db06bafa31aa", + "metadata": {}, + "source": [ + "## Gridplot\n", + "\n", + "Subplots within a `GridPlot` behave the same as simple `Plot` instances! \n", + "\n", + "💡 `Plot` is actually a subclass of `Subplot`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b7e1129-ae8e-4a0f-82dc-bd8fb65871fc", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# GridPlot of shape 2 x 3 with all controllers synced\n", + "grid_plot = fpl.GridPlot(shape=(2, 3), controllers=\"sync\")\n", + "\n", + "# Make a random image graphic for each subplot\n", + "for subplot in grid_plot:\n", + " # create image data\n", + " data = np.random.rand(512, 512)\n", + " # add an image to the subplot\n", + " subplot.add_image(data, name=\"rand-img\")\n", + "\n", + "# Define a function to update the image graphics with new data\n", + "# add_animations will pass the gridplot to the animation function\n", + "def update_data(gp):\n", + " for sp in gp:\n", + " new_data = np.random.rand(512, 512)\n", + " # index the image graphic by name and set the data\n", + " sp[\"rand-img\"].data = new_data\n", + " \n", + "# add the animation function\n", + "grid_plot.add_animations(update_data)\n", + "\n", + "# show the gridplot \n", + "grid_plot.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f4f71c34-3925-442f-bd76-60dd57d09f48", + "metadata": {}, + "source": [ + "### Slicing GridPlot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8194c9e-9a99-4d4a-8984-a4cfcab0c42c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# positional indexing\n", + "# row 0 and col 0\n", + "grid_plot[0, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "d626640f-bc93-4883-9bf4-47b825bbc663", + "metadata": {}, + "source": [ + "You can get the graphics within a subplot, just like with simple `Plot`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bffec80c-e81b-4945-85a2-c2c5e8395677", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[0, 1].graphics" + ] + }, + { + "cell_type": "markdown", + "id": "a4e3184f-c86a-4a7e-b803-31632cc163b0", + "metadata": {}, + "source": [ + "and change their properties" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04b616fb-6644-42ba-8683-0589ce7d165e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[0, 1].graphics[0].vmax = 0.5" + ] + }, + { + "cell_type": "markdown", + "id": "28f7362c-d1b9-43ef-85c5-4d68f70f459c", + "metadata": {}, + "source": [ + "more slicing with `GridPlot`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "920e6365-bb50-4882-9b0d-8367dc485360", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# you can give subplots human-readable string names\n", + "grid_plot[0, 2].name = \"top-right-plot\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73300d2c-3e70-43ad-b5a2-40341b701ac8", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[\"top-right-plot\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "834d9905-35e9-4711-9375-5b1828c80ee2", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# view its position\n", + "grid_plot[\"top-right-plot\"].position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9aa61efa-c6a5-4611-a03b-1b8da66b19f0", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# these are really the same\n", + "grid_plot[\"top-right-plot\"] is grid_plot[0, 2]" + ] + }, + { + "cell_type": "markdown", + "id": "28c8b145-86cb-4445-92be-b7537a87f7ca", + "metadata": {}, + "source": [ + "Indexing with subplot name and graphic name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b7b73a3-5335-4bd5-bbef-c7d3cfbb3ca7", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[\"top-right-plot\"][\"rand-img\"].vmin = 0.5" + ] + }, + { + "cell_type": "markdown", + "id": "6a5b4368-ae4d-442c-a11f-45c70267339b", + "metadata": {}, + "source": [ + "## GridPlot customization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "175d45a6-3351-4b75-8ff3-08797fe0a389", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# grid with 2 rows and 3 columns\n", + "grid_shape = (2, 3)\n", + "\n", + "# pan-zoom controllers for each view\n", + "# views are synced if they have the \n", + "# same controller ID\n", + "controllers = [\n", + " [0, 3, 1], # id each controller with an integer\n", + " [2, 2, 3]\n", + "]\n", + "\n", + "\n", + "# you can give string names for each subplot within the gridplot\n", + "names = [\n", + " [\"subplot0\", \"subplot1\", \"subplot2\"],\n", + " [\"subplot3\", \"subplot4\", \"subplot5\"]\n", + "]\n", + "\n", + "# Create the grid plot\n", + "grid_plot = fpl.GridPlot(\n", + " shape=grid_shape,\n", + " controllers=controllers,\n", + " names=names,\n", + ")\n", + "\n", + "\n", + "# Make a random image graphic for each subplot\n", + "for subplot in grid_plot:\n", + " data = np.random.rand(512, 512)\n", + " # create and add an ImageGraphic\n", + " subplot.add_image(data=data, name=\"rand-image\")\n", + " \n", + "\n", + "# Define a function to update the image graphics \n", + "# with new randomly generated data\n", + "def set_random_frame(gp):\n", + " for subplot in gp:\n", + " new_data = np.random.rand(512, 512)\n", + " subplot[\"rand-image\"].data = new_data\n", + "\n", + "# add the animation\n", + "grid_plot.add_animations(set_random_frame)\n", + "grid_plot.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4224f1c2-5e61-4894-8d72-0519598a3cef", + "metadata": {}, + "source": [ + "Indexing the gridplot to access subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d88dd9b2-9359-42e8-9dfb-96dcbbb34b95", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# can access subplot by name\n", + "grid_plot[\"subplot0\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a14df7ea-14c3-4a8a-84f2-2e2194236d9e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# can access subplot by index\n", + "grid_plot[0, 0]" + ] + }, + { + "cell_type": "markdown", + "id": "5f8a3427-7949-40a4-aec2-38d5d95ef156", + "metadata": {}, + "source": [ + "**subplots also support indexing!**\n", + "\n", + "this can be used to get graphics if they are named" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c99fee0-ce46-4f18-8300-af025c9a967c", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# can access graphic directly via name\n", + "grid_plot[\"subplot0\"][\"rand-image\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ed4eebb7-826d-4856-bbb8-db2de966a0c3", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[\"subplot0\"][\"rand-image\"].vmin = 0.6\n", + "grid_plot[\"subplot0\"][\"rand-image\"].vmax = 0.8" + ] + }, + { + "cell_type": "markdown", + "id": "ad322f6f-e7de-4eb3-a1d9-cf28701a2eae", + "metadata": {}, + "source": [ + "positional indexing also works event if subplots have string names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "759d3966-d92b-460f-ba48-e57adabbf163", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[1, 0][\"rand-image\"].vim = 0.1\n", + "grid_plot[1, 0][\"rand-image\"].vmax = 0.3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a5753b9-ee71-4ed1-bb0d-52bdb4ea365f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid_plot[1, 0][\"rand-image\"].type" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/fastplotlib/__init__.py b/fastplotlib/__init__.py index 999fbe46c..301412aff 100644 --- a/fastplotlib/__init__.py +++ b/fastplotlib/__init__.py @@ -1,6 +1,8 @@ from pathlib import Path from .layouts import Plot, GridPlot +from .graphics import * +from .graphics.selectors import * from wgpu.gui.auto import run diff --git a/fastplotlib/graphics/_base.py b/fastplotlib/graphics/_base.py index dae31c61b..850ef4f89 100644 --- a/fastplotlib/graphics/_base.py +++ b/fastplotlib/graphics/_base.py @@ -391,7 +391,7 @@ def graphics(self) -> np.ndarray[Graphic]: def add_graphic(self, graphic: Graphic, reset_index: False): """Add a graphic to the collection""" - if not isinstance(graphic, self.child_type): + if not type(graphic).__name__ == self.child_type: raise TypeError( f"Can only add graphics of the same type to a collection, " f"You can only add {self.child_type} to a {self.__class__.__name__}, " diff --git a/fastplotlib/graphics/_features/__init__.py b/fastplotlib/graphics/_features/__init__.py index e1cc5dd03..8e78a6260 100644 --- a/fastplotlib/graphics/_features/__init__.py +++ b/fastplotlib/graphics/_features/__init__.py @@ -3,6 +3,7 @@ from ._present import PresentFeature from ._thickness import ThicknessFeature from ._base import GraphicFeature, GraphicFeatureIndexable, FeatureEvent, to_gpu_supported_dtype +from ._selection_features import LinearSelectionFeature, LinearRegionSelectionFeature __all__ = [ "ColorFeature", @@ -17,5 +18,7 @@ "GraphicFeature", "GraphicFeatureIndexable", "FeatureEvent", - "to_gpu_supported_dtype" + "to_gpu_supported_dtype", + "LinearSelectionFeature", + "LinearRegionSelectionFeature", ] diff --git a/fastplotlib/graphics/_features/_base.py b/fastplotlib/graphics/_features/_base.py index 2860d6d4e..1d177c3f4 100644 --- a/fastplotlib/graphics/_features/_base.py +++ b/fastplotlib/graphics/_features/_base.py @@ -6,7 +6,7 @@ import numpy as np -from pygfx import Buffer, Texture +import pygfx supported_dtypes = [ @@ -303,7 +303,7 @@ def _update_range(self, key): @property @abstractmethod - def buffer(self) -> Union[Buffer, Texture]: + def buffer(self) -> Union[pygfx.Buffer, pygfx.Texture]: """Underlying buffer for this feature""" pass diff --git a/fastplotlib/graphics/_features/_colors.py b/fastplotlib/graphics/_features/_colors.py index 55ab13f48..feb349984 100644 --- a/fastplotlib/graphics/_features/_colors.py +++ b/fastplotlib/graphics/_features/_colors.py @@ -1,5 +1,5 @@ import numpy as np -from pygfx import Color +import pygfx from ...utils import make_colors, get_cmap_texture, make_pygfx_colors, parse_cmap_values from ._base import ( @@ -29,7 +29,7 @@ class ColorFeature(GraphicFeatureIndexable): """ @property - def buffer(self): + def buffer(self) -> pygfx.Buffer: return self._parent.world_object.geometry.colors def __getitem__(self, item): @@ -93,11 +93,11 @@ def __init__( f"where the length of the iterable is the same as the number of datapoints." ) - data = np.vstack([np.array(Color(c)) for c in colors]) + data = np.vstack([np.array(pygfx.Color(c)) for c in colors]) # if it's a single RGBA array as a tuple/list elif len(colors) == 4: - c = Color(colors) + c = pygfx.Color(colors) data = np.repeat(np.array([c]), n_colors, axis=0) else: @@ -171,7 +171,7 @@ def __setitem__(self, key, value): new_data_size = len(indices) if not isinstance(value, np.ndarray): - color = np.array(Color(value)) # pygfx color parser + color = np.array(pygfx.Color(value)) # pygfx color parser # make it of shape [n_colors_modify, 4] new_colors = np.repeat( np.array([color]).astype(np.float32), new_data_size, axis=0 diff --git a/fastplotlib/graphics/_features/_data.py b/fastplotlib/graphics/_features/_data.py index 2e8e38c12..0d22299ed 100644 --- a/fastplotlib/graphics/_features/_data.py +++ b/fastplotlib/graphics/_features/_data.py @@ -2,7 +2,7 @@ import numpy as np -from pygfx import Buffer, Texture +import pygfx from ._base import ( GraphicFeatureIndexable, @@ -26,7 +26,7 @@ def __init__(self, parent, data: Any, collection_index: int = None): ) @property - def buffer(self) -> Buffer: + def buffer(self) -> pygfx.Buffer: return self._parent.world_object.geometry.positions def __getitem__(self, item): @@ -116,7 +116,7 @@ def __init__(self, parent, data: Any): super(ImageDataFeature, self).__init__(parent, data) @property - def buffer(self) -> Texture: + def buffer(self) -> pygfx.Texture: """Texture buffer for the image data""" return self._parent.world_object.geometry.grid @@ -167,7 +167,7 @@ def _feature_changed(self, key, new_data): class HeatmapDataFeature(ImageDataFeature): @property - def buffer(self) -> List[Texture]: + def buffer(self) -> List[pygfx.Texture]: """list of Texture buffer for the image data""" return [img.geometry.grid for img in self._parent.world_object.children] diff --git a/fastplotlib/graphics/_features/_present.py b/fastplotlib/graphics/_features/_present.py index 22f42f357..ba257e60b 100644 --- a/fastplotlib/graphics/_features/_present.py +++ b/fastplotlib/graphics/_features/_present.py @@ -18,7 +18,7 @@ class PresentFeature(GraphicFeature): "new_data" ``bool`` new data, ``True`` or ``False`` "collection-index" int the index of the graphic within the collection that triggered the event "world_object" pygfx.WorldObject world object - ==================== ======================== ======================================================================== + ==================== ======================== ========================================================================= """ def __init__(self, parent, present: bool = True, collection_index: int = False): diff --git a/fastplotlib/graphics/_features/_selection_features.py b/fastplotlib/graphics/_features/_selection_features.py new file mode 100644 index 000000000..49dc78b75 --- /dev/null +++ b/fastplotlib/graphics/_features/_selection_features.py @@ -0,0 +1,316 @@ +from typing import Tuple, Union, Any + +import numpy as np + +from ._base import GraphicFeature, FeatureEvent + + +""" +positions for indexing the BoxGeometry to set the "width" and "size" of the box +hacky, but I don't think we can morph meshes in pygfx yet: https://github.com/pygfx/pygfx/issues/346 +""" + +x_right = np.array( + [ + True, + True, + True, + True, + False, + False, + False, + False, + False, + True, + False, + True, + True, + False, + True, + False, + False, + True, + False, + True, + True, + False, + True, + False, + ] +) + +x_left = np.array( + [ + False, + False, + False, + False, + True, + True, + True, + True, + True, + False, + True, + False, + False, + True, + False, + True, + True, + False, + True, + False, + False, + True, + False, + True, + ] +) + +y_top = np.array( + [ + False, + True, + False, + True, + False, + True, + False, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False, + True, + True, + False, + False, + True, + True, + ] +) + +y_bottom = np.array( + [ + True, + False, + True, + False, + True, + False, + True, + False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + False, + False, + True, + True, + False, + False, + ] +) + + + +class LinearSelectionFeature(GraphicFeature): + # A bit much to have a class for this but this allows it to integrate with the fastplotlib callback system + """ + Manages the linear selection and callbacks + + **event pick info** + + =================== =============================== ================================================================================================= + key type selection + =================== =============================== ================================================================================================= + "selected_index" ``int`` the graphic data index that corresponds to the selector position + "world_object" ``pygfx.WorldObject`` pygfx WorldObject + "new_data" ``numpy.ndarray`` or ``None`` the new selector position in world coordinates, not necessarily the same as "selected_index" + "graphic" ``Graphic`` the selector graphic + "delta" ``numpy.ndarray`` the delta vector of the graphic in NDC + "pygfx_event" ``pygfx.Event`` pygfx Event + =================== =============================== ================================================================================================= + + """ + + def __init__(self, parent, axis: str, value: float, limits: Tuple[int, int]): + super(LinearSelectionFeature, self).__init__(parent, data=value) + + self.axis = axis + self.limits = limits + + def _set(self, value: float): + if not (self.limits[0] <= value <= self.limits[1]): + return + + if self.axis == "x": + self._parent.position_x = value + else: + self._parent.position_y = value + + self._data = value + self._feature_changed(key=None, new_data=value) + + def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): + if len(self._event_handlers) < 1: + return + + if self._parent.parent is not None: + g_ix = self._parent.get_selected_index() + else: + g_ix = None + + # get pygfx event and reset it + pygfx_ev = self._parent._pygfx_event + self._parent._pygfx_event = None + + pick_info = { + "world_object": self._parent.world_object, + "new_data": new_data, + "selected_index": g_ix, + "graphic": self._parent, + "pygfx_event": pygfx_ev, + "delta": self._parent.delta, + } + + event_data = FeatureEvent(type="selection", pick_info=pick_info) + + self._call_event_handlers(event_data) + + +class LinearRegionSelectionFeature(GraphicFeature): + """ + Feature for a linearly bounding region + + **event pick info** + + ===================== =============================== ======================================================================================= + key type description + ===================== =============================== ======================================================================================= + "selected_indices" ``numpy.ndarray`` or ``None`` selected graphic data indices + "world_object" ``pygfx.WorldObject`` pygfx World Object + "new_data" ``(float, float)`` current bounds in world coordinates, NOT necessarily the same as "selected_indices". + "graphic" ``Graphic`` the selection graphic + "delta" ``numpy.ndarray`` the delta vector of the graphic in NDC + "pygfx_event" ``pygfx.Event`` pygfx Event + "selected_data" ``numpy.ndarray`` or ``None`` selected graphic data + "move_info" ``MoveInfo`` last position and event source (pygfx.Mesh or pygfx.Line) + ===================== =============================== ======================================================================================= + + """ + + def __init__( + self, parent, selection: Tuple[int, int], axis: str, limits: Tuple[int, int] + ): + super(LinearRegionSelectionFeature, self).__init__(parent, data=selection) + + self._axis = axis + self.limits = limits + + self._set(selection) + + @property + def axis(self) -> str: + """one of "x" | "y" """ + return self._axis + + def _set(self, value: Tuple[float, float]): + # sets new bounds + if not isinstance(value, tuple): + raise TypeError( + "Bounds must be a tuple in the form of `(min_bound, max_bound)`, " + "where `min_bound` and `max_bound` are numeric values." + ) + + # make sure bounds not exceeded + for v in value: + if not (self.limits[0] <= v <= self.limits[1]): + return + + # make sure `selector width >= 2`, left edge must not move past right edge! + # or bottom edge must not move past top edge! + # has to be at least 2 otherwise can't join datapoints for lines + if not (value[1] - value[0]) >= 2: + return + + if self.axis == "x": + # change left x position of the fill mesh + self._parent.fill.geometry.positions.data[x_left, 0] = value[0] + + # change right x position of the fill mesh + self._parent.fill.geometry.positions.data[x_right, 0] = value[1] + + # change x position of the left edge line + self._parent.edges[0].geometry.positions.data[:, 0] = value[0] + + # change x position of the right edge line + self._parent.edges[1].geometry.positions.data[:, 0] = value[1] + + elif self.axis == "y": + # change bottom y position of the fill mesh + self._parent.fill.geometry.positions.data[y_bottom, 1] = value[0] + + # change top position of the fill mesh + self._parent.fill.geometry.positions.data[y_top, 1] = value[1] + + # change y position of the bottom edge line + self._parent.edges[0].geometry.positions.data[:, 1] = value[0] + + # change y position of the top edge line + self._parent.edges[1].geometry.positions.data[:, 1] = value[1] + + self._data = value # (value[0], value[1]) + + # send changes to GPU + self._parent.fill.geometry.positions.update_range() + + self._parent.edges[0].geometry.positions.update_range() + self._parent.edges[1].geometry.positions.update_range() + + # calls any events + self._feature_changed(key=None, new_data=value) + + def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): + if len(self._event_handlers) < 1: + return + + if self._parent.parent is not None: + selected_ixs = self._parent.get_selected_indices() + selected_data = self._parent.get_selected_data() + else: + selected_ixs = None + selected_data = None + + # get pygfx event and reset it + pygfx_ev = self._parent._pygfx_event + self._parent._pygfx_event = None + + pick_info = { + "world_object": self._parent.world_object, + "new_data": new_data, + "selected_indices": selected_ixs, + "selected_data": selected_data, + "graphic": self._parent, + "delta": self._parent.delta, + "pygfx_event": pygfx_ev, + "move_info": self._parent._move_info, + } + + event_data = FeatureEvent(type="selection", pick_info=pick_info) + + self._call_event_handlers(event_data) diff --git a/fastplotlib/graphics/_features/_thickness.py b/fastplotlib/graphics/_features/_thickness.py index b970d298e..cae3828b7 100644 --- a/fastplotlib/graphics/_features/_thickness.py +++ b/fastplotlib/graphics/_features/_thickness.py @@ -7,14 +7,14 @@ class ThicknessFeature(GraphicFeature): **event pick info:** - ===================== ======================== ========================================================================= + ==================== ======================== ========================================================================= key type description ==================== ======================== ========================================================================= "index" ``None`` not used "new_data" ``float`` new thickness value "collection-index" int the index of the graphic within the collection that triggered the event "world_object" pygfx.WorldObject world object - ==================== ======================== ======================================================================== + ==================== ======================== ========================================================================= """ def __init__(self, parent, thickness: float): diff --git a/fastplotlib/graphics/line_collection.py b/fastplotlib/graphics/line_collection.py index 860a2e74a..ae2ec64d4 100644 --- a/fastplotlib/graphics/line_collection.py +++ b/fastplotlib/graphics/line_collection.py @@ -14,7 +14,7 @@ class LineCollection(GraphicCollection, Interaction): - child_type = LineGraphic + child_type = LineGraphic.__name__ feature_events = ("data", "colors", "cmap", "thickness", "present") def __init__( diff --git a/fastplotlib/graphics/selectors/_linear.py b/fastplotlib/graphics/selectors/_linear.py index 88b0ac523..39710305d 100644 --- a/fastplotlib/graphics/selectors/_linear.py +++ b/fastplotlib/graphics/selectors/_linear.py @@ -12,76 +12,11 @@ except (ImportError, ModuleNotFoundError): HAS_IPYWIDGETS = False -from .._base import Graphic, GraphicFeature, GraphicCollection -from .._features import FeatureEvent +from .._base import Graphic, GraphicCollection +from .._features._selection_features import LinearSelectionFeature from ._base_selector import BaseSelector -class LinearSelectionFeature(GraphicFeature): - # A bit much to have a class for this but this allows it to integrate with the fastplotlib callback system - """ - Manages the linear selection and callbacks - - **event pick info** - - =================== =============================== ================================================================================================= - key type selection - =================== =============================== ================================================================================================= - "selected_index" ``int`` the graphic data index that corresponds to the selector position - "world_object" ``pygfx.WorldObject`` pygfx WorldObject - "new_data" ``numpy.ndarray`` or ``None`` the new selector position in world coordinates, not necessarily the same as "selected_index" - "graphic" ``Graphic`` the selector graphic - "delta" ``numpy.ndarray`` the delta vector of the graphic in NDC - "pygfx_event" ``pygfx.Event`` pygfx Event - =================== =============================== ================================================================================================= - - """ - - def __init__(self, parent, axis: str, value: float, limits: Tuple[int, int]): - super(LinearSelectionFeature, self).__init__(parent, data=value) - - self.axis = axis - self.limits = limits - - def _set(self, value: float): - if not (self.limits[0] <= value <= self.limits[1]): - return - - if self.axis == "x": - self._parent.position_x = value - else: - self._parent.position_y = value - - self._data = value - self._feature_changed(key=None, new_data=value) - - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): - if len(self._event_handlers) < 1: - return - - if self._parent.parent is not None: - g_ix = self._parent.get_selected_index() - else: - g_ix = None - - # get pygfx event and reset it - pygfx_ev = self._parent._pygfx_event - self._parent._pygfx_event = None - - pick_info = { - "world_object": self._parent.world_object, - "new_data": new_data, - "selected_index": g_ix, - "graphic": self._parent, - "pygfx_event": pygfx_ev, - "delta": self._parent.delta, - } - - event_data = FeatureEvent(type="selection", pick_info=pick_info) - - self._call_event_handlers(event_data) - - class LinearSelector(Graphic, BaseSelector): # TODO: make `selection` arg in graphics data space not world space def __init__( @@ -137,10 +72,13 @@ def __init__( Features -------- - selection: :class:`LinearSelectionFeature` - ``selection()`` returns the current slider position in world coordinates - use ``selection.add_event_handler()`` to add callback functions that are - called when the LinearSelector selection changes. See feature class for event pick_info table + selection: :class:`.LinearSelectionFeature` + ``selection()`` returns the current selector position in world coordinates. + Use ``get_selected_index()`` to get the currently selected index in data + space. + Use ``selection.add_event_handler()`` to add callback functions that are + called when the LinearSelector selection changes. See feature class for + event pick_info table """ if len(limits) != 2: diff --git a/fastplotlib/graphics/selectors/_linear_region.py b/fastplotlib/graphics/selectors/_linear_region.py index a9a0479b4..0759cd4fc 100644 --- a/fastplotlib/graphics/selectors/_linear_region.py +++ b/fastplotlib/graphics/selectors/_linear_region.py @@ -4,132 +4,8 @@ import pygfx from .._base import Graphic, GraphicCollection -from .._features import GraphicFeature, FeatureEvent from ._base_selector import BaseSelector -from ._mesh_positions import x_right, x_left, y_top, y_bottom - - -class LinearRegionSelectionFeature(GraphicFeature): - """ - Feature for a linearly bounding region - - **event pick info** - - +--------------------+-------------------------------+--------------------------------------------------------------------------------------+ - | key | type | description | - +====================+===============================+======================================================================================+ - | "selected_indices" | ``numpy.ndarray`` or ``None`` | selected graphic data indices | - | "world_object" | ``pygfx.WorldObject`` | pygfx World Object | - | "new_data" | ``(float, float)`` | current bounds in world coordinates, NOT necessarily the same as "selected_indices". | - | "graphic" | ``Graphic`` | the selection graphic | - | "delta" | ``numpy.ndarray`` | the delta vector of the graphic in NDC | - | "pygfx_event" | ``pygfx.Event`` | pygfx Event | - | "selected_data" | ``numpy.ndarray`` or ``None`` | selected graphic data | - | "move_info" | ``MoveInfo`` | last position and event source (pygfx.Mesh or pygfx.Line) | - +--------------------+-------------------------------+--------------------------------------------------------------------------------------+ - - """ - - def __init__( - self, parent, selection: Tuple[int, int], axis: str, limits: Tuple[int, int] - ): - super(LinearRegionSelectionFeature, self).__init__(parent, data=selection) - - self._axis = axis - self.limits = limits - - self._set(selection) - - @property - def axis(self) -> str: - """one of "x" | "y" """ - return self._axis - - def _set(self, value: Tuple[float, float]): - # sets new bounds - if not isinstance(value, tuple): - raise TypeError( - "Bounds must be a tuple in the form of `(min_bound, max_bound)`, " - "where `min_bound` and `max_bound` are numeric values." - ) - - # make sure bounds not exceeded - for v in value: - if not (self.limits[0] <= v <= self.limits[1]): - return - - # make sure `selector width >= 2`, left edge must not move past right edge! - # or bottom edge must not move past top edge! - # has to be at least 2 otherwise can't join datapoints for lines - if not (value[1] - value[0]) >= 2: - return - - if self.axis == "x": - # change left x position of the fill mesh - self._parent.fill.geometry.positions.data[x_left, 0] = value[0] - - # change right x position of the fill mesh - self._parent.fill.geometry.positions.data[x_right, 0] = value[1] - - # change x position of the left edge line - self._parent.edges[0].geometry.positions.data[:, 0] = value[0] - - # change x position of the right edge line - self._parent.edges[1].geometry.positions.data[:, 0] = value[1] - - elif self.axis == "y": - # change bottom y position of the fill mesh - self._parent.fill.geometry.positions.data[y_bottom, 1] = value[0] - - # change top position of the fill mesh - self._parent.fill.geometry.positions.data[y_top, 1] = value[1] - - # change y position of the bottom edge line - self._parent.edges[0].geometry.positions.data[:, 1] = value[0] - - # change y position of the top edge line - self._parent.edges[1].geometry.positions.data[:, 1] = value[1] - - self._data = value # (value[0], value[1]) - - # send changes to GPU - self._parent.fill.geometry.positions.update_range() - - self._parent.edges[0].geometry.positions.update_range() - self._parent.edges[1].geometry.positions.update_range() - - # calls any events - self._feature_changed(key=None, new_data=value) - - def _feature_changed(self, key: Union[int, slice, Tuple[slice]], new_data: Any): - if len(self._event_handlers) < 1: - return - - if self._parent.parent is not None: - selected_ixs = self._parent.get_selected_indices() - selected_data = self._parent.get_selected_data() - else: - selected_ixs = None - selected_data = None - - # get pygfx event and reset it - pygfx_ev = self._parent._pygfx_event - self._parent._pygfx_event = None - - pick_info = { - "world_object": self._parent.world_object, - "new_data": new_data, - "selected_indices": selected_ixs, - "selected_data": selected_data, - "graphic": self._parent, - "delta": self._parent.delta, - "pygfx_event": pygfx_ev, - "move_info": self._parent._move_info, - } - - event_data = FeatureEvent(type="selection", pick_info=pick_info) - - self._call_event_handlers(event_data) +from .._features._selection_features import LinearRegionSelectionFeature class LinearRegionSelector(Graphic, BaseSelector): @@ -191,6 +67,18 @@ def __init__( name: str name for this selector graphic + + Features + -------- + + selection: :class:`.LinearRegionSelectionFeature` + ``selection()`` returns the current selector bounds in world coordinates. + Use ``get_selected_indices()`` to return the selected indices in data + space, and ``get_selected_data()`` to return the selected data. + Use ``selection.add_event_handler()`` to add callback functions that are + called when the LinearSelector selection changes. See feature class for + event pick_info table. + """ # lots of very close to zero values etc. so round them diff --git a/fastplotlib/graphics/selectors/_mesh_positions.py b/fastplotlib/graphics/selectors/_mesh_positions.py index e7cd5ae93..07ff60498 100644 --- a/fastplotlib/graphics/selectors/_mesh_positions.py +++ b/fastplotlib/graphics/selectors/_mesh_positions.py @@ -1,123 +1,2 @@ import numpy as np - -""" -positions for indexing the BoxGeometry to set the "width" and "size" of the box -hacky, but I don't think we can morph meshes in pygfx yet: https://github.com/pygfx/pygfx/issues/346 -""" - -x_right = np.array( - [ - True, - True, - True, - True, - False, - False, - False, - False, - False, - True, - False, - True, - True, - False, - True, - False, - False, - True, - False, - True, - True, - False, - True, - False, - ] -) - -x_left = np.array( - [ - False, - False, - False, - False, - True, - True, - True, - True, - True, - False, - True, - False, - False, - True, - False, - True, - True, - False, - True, - False, - False, - True, - False, - True, - ] -) - -y_top = np.array( - [ - False, - True, - False, - True, - False, - True, - False, - True, - True, - True, - True, - True, - False, - False, - False, - False, - False, - False, - True, - True, - False, - False, - True, - True, - ] -) - -y_bottom = np.array( - [ - True, - False, - True, - False, - True, - False, - True, - False, - False, - False, - False, - False, - True, - True, - True, - True, - True, - True, - False, - False, - True, - True, - False, - False, - ] -) diff --git a/fastplotlib/layouts/_gridplot.py b/fastplotlib/layouts/_gridplot.py index 3e9a16aac..5983abe1b 100644 --- a/fastplotlib/layouts/_gridplot.py +++ b/fastplotlib/layouts/_gridplot.py @@ -5,6 +5,7 @@ from typing import * from inspect import getfullargspec from warnings import warn +import os import pygfx @@ -77,6 +78,7 @@ def __init__( starting size of canvas, default (500, 300) """ + self.shape = shape self.toolbar = None @@ -315,6 +317,11 @@ def show( _maintain_aspect = maintain_aspect subplot.auto_scale(maintain_aspect=_maintain_aspect, zoom=0.95) + if "NB_SNAPSHOT" in os.environ.keys(): + # used for docs + if os.environ["NB_SNAPSHOT"] == "1": + return self.canvas.snapshot() + # check if in jupyter notebook, or if toolbar is False if (self.canvas.__class__.__name__ != "JupyterWgpuCanvas") or (not toolbar): return self.canvas diff --git a/fastplotlib/layouts/_plot.py b/fastplotlib/layouts/_plot.py index 52caf9cce..a8d61aa19 100644 --- a/fastplotlib/layouts/_plot.py +++ b/fastplotlib/layouts/_plot.py @@ -1,6 +1,7 @@ from typing import * from datetime import datetime import traceback +import os import pygfx from wgpu.gui.auto import WgpuCanvas, is_jupyter @@ -139,6 +140,11 @@ def show( if autoscale: self.auto_scale(maintain_aspect=maintain_aspect, zoom=0.95) + if "NB_SNAPSHOT" in os.environ.keys(): + # used for docs + if os.environ["NB_SNAPSHOT"] == "1": + return self.canvas.snapshot() + # check if in jupyter notebook, or if toolbar is False if (self.canvas.__class__.__name__ != "JupyterWgpuCanvas") or (not toolbar): return self.canvas diff --git a/setup.py b/setup.py index f06183a60..2616093fc 100644 --- a/setup.py +++ b/setup.py @@ -11,10 +11,15 @@ extras_require = { "docs": [ "sphinx", - "pydata-sphinx-theme<0.10.0", + "furo", "glfw", "jupyter-rfb>=0.4.1", # required so ImageWidget docs show up - "ipywidgets>=8.0.0,<9" + "ipywidgets>=8.0.0,<9", + "sphinx-copybutton", + "sphinx-design", + "nbsphinx", + "pandoc", + "jupyterlab" ], "notebook":