{ "cells": [ { "cell_type": "markdown", "metadata": { "nbsphinx": "hidden" }, "source": [ "This notebook is part of the `clifford` documentation: https://clifford.readthedocs.io/." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Visualization tools\n", "\n", "In this example we will look at some external tools that can be used with `clifford` to help visualize geometric objects.\n", "\n", "The two tools available are:\n", "\n", "* `pyganja` ([github](https://github.com/pygae/pyganja))\n", "* `mpl_toolkits.clifford` ([github](https://github.com/pygae/mpl_toolkits.clifford))\n", "\n", "Both of these can be installed with `pip install` followed by the package name above." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## G2C\n", "\n", "Let's start by creating some objects in 2d Conformal Geometric Algebra to visualize:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from clifford.g2c import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "point = up(2*e1+e2)\n", "line = up(3*e1 + 2*e2) ^ up(3*e1 - 2*e2) ^ einf\n", "circle = up(e1) ^ up(-e1 + 2*e2) ^ up(-e1 - 2*e2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll create copies of the point and line reflected in the circle, using $X = C\\hat X\\tilde C$, where $\\hat X$ is the grade involution." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "point_refl = circle * point.gradeInvol() * ~circle\n", "line_refl = circle * line.gradeInvol() * ~circle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ``pyganja``\n", "\n", "pyganja is a python interface to the `ganja.js` ([github](https://github.com/enkimute/ganja.js)) library.\n", "To use it, typically we need to import two names from the library:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pyganja import GanjaScene, draw\n", "import pyganja; pyganja.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`GanjaScene` lets us build scenes out of geometric objects, with attached labels and RGB colors:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc = GanjaScene()\n", "sc.add_object(point, color=(255, 0, 0), label='point')\n", "sc.add_object(line, color=(0, 255, 0), label='line')\n", "sc.add_object(circle, color=(0, 0, 255), label='circle')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc_refl = GanjaScene()\n", "sc_refl.add_object(point_refl, color=(128, 0, 0), label='point_refl')\n", "sc_refl.add_object(line_refl, color=(0, 128, 0), label='line_refl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once we've built our scene, we can `draw` it, specifying a `scale` (which here we use to zoom out), and the signature of our algebra (which defaults to conformal 3D):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "draw(sc, sig=layout.sig, scale=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A cool feature of ``GanjaScene`` is the ability to use `+` to draw both scenes together:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "draw(sc + sc_refl, sig=layout.sig, scale=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `mpl_toolkits.clifford`\n", "\n", "While `ganja.js` produces great diagrams, it's hard to combine them with other plotting tools.\n", "`mpl_toolkits.clifford` works within `matplotlib`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from matplotlib import pyplot as plt\n", "plt.ioff() # we'll ask for plotting when we want it\n", "\n", "# if you're editing this locally, you'll get an interactive UI if you uncomment the following\n", "#\n", "# %matplotlib notebook\n", "\n", "from mpl_toolkits.clifford import plot\n", "import mpl_toolkits.clifford; mpl_toolkits.clifford.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assembling the plot is a lot more work, but we also get much more control:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# standard matplotlib stuff - construct empty plots side-by-side, and set the scaling\n", "fig, (ax_before, ax_both) = plt.subplots(1, 2, sharex=True, sharey=True)\n", "ax_before.set(xlim=[-4, 4], ylim=[-4, 4], aspect='equal')\n", "ax_both.set(xlim=[-4, 4], ylim=[-4, 4], aspect='equal')\n", "\n", "# plot the objects before reflection on both plots\n", "for ax in (ax_before, ax_both):\n", " plot(ax, [point], color='tab:blue', label='point', marker='x', linestyle=' ')\n", " plot(ax, [line], color='tab:green', label='line')\n", " plot(ax, [circle], color='tab:red', label='circle')\n", "\n", "# plot the objects after reflection, with thicker lines\n", "plot(ax_both, [point_refl], color='tab:blue', label='point_refl', marker='x', linestyle=' ', markeredgewidth=2)\n", "plot(ax_both, [line_refl], color='tab:green', label='line_refl', linewidth=2)\n", "\n", "fig.tight_layout()\n", "ax_both.legend()\n", "\n", "# show the figure\n", "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## G3C\n", "\n", "Let's repeat the above, but with 3D Conformal Geometric Algebra.\n", "Note that if you're viewing these docs in a jupyter notebook, the lines below will replace all your 2d variables with 3d ones" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from clifford.g3c import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "point = up(2*e1+e2)\n", "line = up(3*e1 + 2*e2) ^ up(3*e1 - 2*e2) ^ einf\n", "circle = up(e1) ^ up(-e1 + 1.6*e2 + 1.2*e3) ^ up(-e1 - 1.6*e2 - 1.2*e3)\n", "sphere = up(3*e1) ^ up(e1) ^ up(2*e1 + e2) ^ up(2*e1 + e3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# note that due to floating point rounding, we need to truncate back to a single grade here, with ``(grade)``\n", "point_refl = homo((circle * point.gradeInvol() * ~circle)(1))\n", "line_refl = (circle * line.gradeInvol() * ~circle)(3)\n", "sphere_refl = (circle * sphere.gradeInvol() * ~circle)(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ``pyganja``" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once again, we can create a pair of scenes exactly as before" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc = GanjaScene()\n", "sc.add_object(point, color=(255, 0, 0), label='point')\n", "sc.add_object(line, color=(0, 255, 0), label='line')\n", "sc.add_object(circle, color=(0, 0, 255), label='circle')\n", "sc.add_object(sphere, color=(0, 255, 255), label='sphere')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc_refl = GanjaScene()\n", "sc_refl.add_object(point_refl, color=(128, 0, 0), label='point_refl')\n", "sc_refl.add_object(line_refl.normal(), color=(0, 128, 0), label='line_refl')\n", "sc_refl.add_object(sphere_refl.normal(), color=(0, 128, 128), label='sphere_refl')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But this time, when we draw them we don't need to pass `sig`.\n", "Better yet, we can rotate the 3D world around using left click, pan with right click, and zoom with the scroll wheel." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "draw(sc + sc_refl, scale=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some more example of using `pyganja` to visualize 3D CGA can be found in the [interpolation](./interpolation.ipynb) and [clustering](./clustering.ipynb) notebooks." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `mpl_toolkits.clifford`\n", "\n", "The 3D approach for `matplotlib` is much the same.\n", "Note that due to poor handling of rounding errors in `clifford.tools.classify`, a call to `.normal()` is needed.\n", "Along with explicit grade selection, this is a useful trick to try and get something to render which otherwise would not." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# standard matplotlib stuff - construct empty plots side-by-side, and set the scaling\n", "fig, (ax_before, ax_both) = plt.subplots(1, 2, subplot_kw=dict(projection='3d'), figsize=(8, 4))\n", "ax_before.set(xlim=[-4, 4], ylim=[-4, 4], zlim=[-4, 4])\n", "ax_both.set(xlim=[-4, 4], ylim=[-4, 4], zlim=[-4, 4])\n", "\n", "# plot the objects before reflection on both plots\n", "for ax in (ax_before, ax_both):\n", " plot(ax, [point], color='tab:red', label='point', marker='x', linestyle=' ')\n", " plot(ax, [line], color='tab:green', label='line')\n", " plot(ax, [circle], color='tab:blue', label='circle')\n", " plot(ax, [sphere], color='tab:cyan') # labels do not work for spheres: pygae/mpl_toolkits.clifford#5\n", "\n", "# plot the objects after reflection\n", "plot(ax_both, [point_refl], color='tab:red', label='point_refl', marker='x', linestyle=' ', markeredgewidth=2)\n", "plot(ax_both, [line_refl.normal()], color='tab:green', label='line_refl', linewidth=2)\n", "plot(ax_both, [sphere_refl], color='tab:cyan')\n", "\n", "fig.tight_layout()\n", "ax_both.legend()\n", "\n", "# show the figure\n", "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some more example of using `mpl_toolkits.clifford` to visualize 3D CGA can be found in the `examples` folder of the `mpl_toolkits.clifford` repositiory, [here](https://github.com/pygae/mpl_toolkits.clifford/tree/master/examples)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.8.0" } }, "nbformat": 4, "nbformat_minor": 2 }