.. _stereographic_projection: Möbius Pencil of Circles ======================== .. image:: rendered_planar_pencil.png :width: 800px :align: center In this tutorial, our aim is to visualize pencils of Möbius circles. This is explained below, expecting a bit of basic knowledge of geometry. If you are not that interested, or do not completely understand the mathematical background - that is okay too. You can still go on with the tutorial. The nice thing about visualization is, it helps us understand what is happening. .. image:: mob_pencil_1_and_2.png :width: 800px :align: center In the following, we explain how to create this python script: :download:`Möbius Pencil of Circles Script <../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py>`, and use it to visualize the objects in Blender. .. contents:: Table of contents :local: :backlinks: none Mathematical Background ----------------------- What is a **Möbius pencil of circles**? Möbius geometry in :math:`\widehat{\mathbb{R}^2} = \mathbb{R}^{2} \cup \{\infty\}` corresponds to geometry on the unit sphere :math:`S^2` via stereographic projection. Thus, circles in two-dimensional Möbius geometry correspond to circles on :math:`S^2`, which are intersections of :math:`S^2` with planes. By polarity, a plane intersecting :math:`S^2` corresponds to a point outside :math:`S^2`. If we now consider a line in :math:`\mathbb{R}^3`, by polarity its points define a one-parameter family of planes in :math:`\mathbb{R}^3` and therefore a one-parameter family of spherical circles, and hence - via stereographic projection - a so called Möbius pencil of circles in :math:`\widehat{\mathbb{R}^2}`. The one-parameter family of planes all intersect in a common line, the polar line of the given line, which again represents a Möbius pencil of circles. In :math:`\widehat{\mathbb{R}^2}` this polar pencil represents a family of circles where each circle intersects all circles in the first pencil orthogonally. Setup ----- Start Blender from the terminal, so you can read error messages. More on how to start and setup Blender, you find here: :ref:`blender_getting_started`. In the Blender GUI, create a new script, add the following imports to your new script, and execute with "Alt"+"P". .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [imports] :end-before: [imports] To start with a clean Blender file, we first remove all objects from the scene with :py:meth:`~ddg.visualization.blender.scene.clear`. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [clear] :end-before: [clear] For more on the **clear functions**, see :ref:`blender_utils_clear`. In this script, our aim is to generate a lot of Blender objects, and we want to be able to display them independently from one another. To achieve this, we will use **Blender collections**, enabling us to show/hide a lot of objects simultaneously. New collections can be created with the following command: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [collection setup] :end-before: [collection setup] You can pass a list of `children` to the function :py:meth:`~ddg.blender.collection.collection`. If an entry of the list is a string, it will create a child with corresponding name. If an entry of the list is a list of strings, the first string determines the name of the child collection, the others will create grandchildren, i.e children of the child's collection. If the parent collection already exists, it returns that collection object, ignoring the children. We are ready to create some objects. Sphere and Line --------------- **Sphere** First, we want to **create the sphere** as a :py:class:`~ddg.geometry.Quadric`. Add the following to your script. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [create sphere as quadric] :end-before: [create sphere as quadric] A :py:class:`~ddg.geometry.Quadric` is created by its symmetric matrix. More information on Quadrics, you can find :ref:`here `. To display the sphere in Blender, we have to **convert it to a Blender object**. This is easily done with the **conversion function** :py:meth:`~ddg.blender.convert`! .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [display sphere] :end-before: [display sphere] As for any smooth object, for the quadric to be displayed in Blender, it needs to be discretized using a **sampling**. The function :py:meth:`~ddg.blender.convert` has a built-in sampling for quadrics which should produce a nice-looking result in most cases. If you want more control over how the result is going to look, you will have to sample it yourself. For this, you will first need to convert the object to a :py:class:`~ddg.nets.SmoothNet`. This can then, by a sampling, be converted to a :py:class:`~ddg.nets.DiscreteNet`. For more information, have a look at the :ref:`nets ` user's guide. The sampling options are explained :ref:`here `. The given name determines the name of the object in Blender. .. image:: sphere0.png :width: 600px :align: center **Line** Next, we want to create and display a **line**. For that, we use :py:class:`~ddg.geometry.Subspace`, which is a class for projective subspaces. Here we give two examples one line that intersects the sphere and another line that is tangential to the sphere. With these two you can generate the configurations of the images at the top. So let's choose .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [create line as subspace] :end-before: [create line as subspace] A subspace can be simply defined by the vectors that span it. Note, that we want a line in 3D projective space, which corresponds to a 2D vector subspace of :math:`\mathbb{R}^4`. That's why we have two 4D vectors. More on subspaces, you can find :ref:`here `. Next, we create a :py:class:`~ddg.nets.SmoothNet` out of the subspace, to obtain an affine image of the projective subspace, that is a **line in 3D space**. Before we do so, we orthonormalize and center the subspace. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [line to smooth net] :end-before: [line to smooth net] The argument `affine` is given to obtain an affine image of the projective line. The `convex=True` argument means, that the net function of the resulting SmoothNet is the convex combination of the vectors spanning the subspace. By setting the domain of the SmoothNet to `domain=[[-4,4]]`, we obtain a line segment symmetrical around the center of the line. And finally, we convert it to a Blender object by: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [line to blender object] :end-before: [line to blender object] The given `domain` is interpreted as domain of the net function and determines the length of the line. We add the line to the `collection` we created. The last argument, given in this form of a dictionary, determines the thickness of the line. Feel free to play with it. .. image:: sphere1.png :width: 600px :align: center Spherical and Planar Circles ---------------------------- Next, we will see how a point on the line l corresponds to a circle on s2, and how the spheric circle corresponds to a planar circle. **Spherical Circles** For a given point, we want to calculate its **polar plane** with respect to s2, as well as the **intersection** of the plane with s2. These intersection should then be converted to **blender objects**, so that we can see them. And this gives us our spherical circles. We choose a point on our line l. When given a point of its domain as an argument, a :py:class:`~ddg.nets.SmoothNet` returns its value at this point: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [get a point] :end-before: [get a point] Now, we want to display the sampled point in Blender, as a little sphere. The sampled point is just an affine image of a point on l. We homogenize it, .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [homogenize point] :end-before: [homogenize point] and convert it to a :py:class:`~ddg.geometry.Subspace`. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [convert point to subspace] :end-before: [convert point to subspace] We give the radius for the size of the sphere that visualizes the point. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [display point] :end-before: [display point] Now, we can make use of the methods for Subspaces and calculate the polar plane .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [calculate polar plane] :end-before: [calculate polar plane] and calculate the intersection of it with the sphere. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [calculate intersection circle] :end-before: [calculate intersection circle] Trying to convert an empty intersection to a blender object yields an error, so first, we check if the intersection is empty by checking its dimension. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [check for empty intersection] :end-before: [check for empty intersection] This gives us: .. image:: sphere2.png :width: 600px :align: center **Planar Circles** Next, we want to stereographically project the spherical circle onto the plane. For this we create a plane to project on as a subspace and stereographically project the circle: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [stereographic project and display circle] :end-before: [stereographic project and display circle] In general the function :py:func:`~ddg.geometry.stereographic_project` takes the point to project from and the plane to project to as arguments. In our case the projection point, the north pole, is precisely the default argument of the function. And we get: .. image:: sphere3.png :width: 600px :align: center **Alternatively: Shadows** We can also obtain the planar circles in a different way, putting a Blender light inside the sphere and looking at its shadow. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [stereographic project with a light] :end-before: [stereographic project with a light] Here you may want to move the plane `euc_plane` a little bit lower so it does not intersect s2 so you can see the shadow of all circles. If you want to see the shadow in Blender you have to select the `Rendered Viewport Shading`. .. image:: rendered_viewport.png :width: 600px :align: center Möbius Pencil of Circles ------------------------ If we now consider the corresponding circles for all points on a line, that is called a **Möbius pencil of circles**. To visualize this pencil, we need to choose some points on the line, and then for each point proceed as above. **Circles Function for a Point** First, we write a function that takes a point and creates the corresponding spherical and planar circle. This function does the job: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :pyobject: circles_from_point **Wrapper Function for Points on a Line** Get the corresponding circles for a point on l by: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [get circles for a point] :end-before: [get circles for a point] **Adding a Slider for a Varying Point on Line** Next, we will add a slider to the blender GUI, with which we can regulate certain parameters in the creation of our objects. This is explained in more detail in :ref:`this ` docs entry. In our case, we want to vary the parameter which determines the point on the line l. The following code will add such a slider: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [add slider] :end-before: [add slider] To find the slider in the blender GUI, go to the viewport, press "N" to open the side menu, and go to the "DDG" tab. There it is. .. image:: find_slider.png :width: 600px :align: center Change the parameter t, to see how the point and the circles change! But **be careful** sliders don't work (well) with collections as they modify collections themselves. Its better not to work with collections when using sliders. **Visualize some Circles of the Pencil** To get a nice image of the pencil, we can sample points on the corresponding line, and then plot the circles for each of the points. First, we need a sampling of the domain of the line. That is, a list with numbers. Let's use linear sampling here (equidistant points). Of course you could try something else at home. It's also fun to play with the number of samples. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [sample domain of line] :end-before: [sample domain of line] In the sampling, the first two arguments correspond to the domain of the lines, and the third is the number of samples. Now, we can easily create some circles in the pencil: .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [visualize pencil] :end-before: [visualize pencil] It is very useful, to pass collections here, so we can hide and show the different groups of objects as we wish. For example, hiding the collections with the points gives us: .. image:: visualize_pencil.png :width: 600px :align: center Nice! Orthogonal Pencil ----------------- Consider the Möbius pencil of circles for the **polar line** of l. All circles of this pencil intersect all circles in the pencil corresponding to l orthogonally. With pyddg, we can easily polarize the :py:class:`~ddg.geometry.Subspace` l with respect to the :py:class:`~ddg.geometry.Quadric` s2 to obtain the polar line as a :py:class:`~ddg.geometry.Subspace`. We then proceed as before, to create the corresponding orthogonal pencil! .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [orthogonal pencil] :end-before: [orthogonal pencil] And we get: .. image:: visualize_orth_pencil.png :width: 600px :align: center Render an Image --------------- Before we render an image, we need to add a **camera and light** to the Blender scene and create materials for our objects. .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [camera and light] :end-before: [camera and light] How to create such a setup and **render** an image is further explained here :ref:`camera_and_light` and here :ref:`blender_rendering`. **Materials** can be created and assigned to objects in the blender GUI. If you have a lot of objects, it's convenient to assign materials via script! Basic materials can also be created in the script, as explained here: :ref:`Creating and Setting Materials`. We add the following materials in the beginning of our script, before object creation. (The names of the materials agree with the names of the materials we use in object creation). .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [materials] :end-before: [materials] You choose which collections (or objects) to **hide** and **show** for an image. In the python script, collections can be hidden with .. literalinclude:: ../../../../examples/blender/docs/tutorials/moebius_pencil_of_circles.py :language: python :start-after: [hide collections] :end-before: [hide collections] Keep in mind that there are two parameters, `hide_viewport` and `hide_render`, that can be set. Here, we have hidden certain collections so that when executing the script, the resulting image is less cluttered. Now by **hiding** and **showing** different collections in the blend file we created, and playing with the materials, light and camera, we can then render beautiful images, like: .. image:: rendered_planar_pencil.png :width: 800px :align: center (Serving suggestion)