.. _visualizing_nets: Visualizing Nets ================ .. seealso:: :ref:`Visualizing ddg objects in Blender ` for a general guide on visualization of ddg objects in Blender and usage of :py:func:`~ddg.conversion.blender.core.to_blender_object` and :py:func:`~ddg.conversion.blender.core.to_blender_object_helper`. For visualization in Blender, a :py:class:`~ddg.datastructures.nets.net.SmoothNet` (or a :py:class:`~ddg.datastructures.nets.net.SmoothCurve`), has to be converted to a :py:class:`~ddg.datastructures.nets.net.DiscreteNet` (or a :py:class:`~ddg.datastructures.nets.net.DiscreteCurve`), which is achieved by sampling. This can be done manually, using :py:func:`~ddg.conversion.nets.core.to_smooth_net`, and the :py:class:`~ddg.datastructures.nets.net.DiscreteNet` can be handed to :py:func:`~ddg.conversion.blender.core.to_blender_object`. Alternatively, the :py:class:`~ddg.datastructures.nets.net.SmoothNet` can be handed to :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` which combines the sampling and the call of :py:func:`~ddg.conversion.blender.core.to_blender_object`. .. contents:: :local: :backlinks: none Using to_blender_object ----------------------- A :py:class:`~ddg.datastructures.nets.net.SmoothNet` can be converted to a :py:class:`~ddg.datastructures.nets.net.DiscreteNet` via sampling. The sampling is passed to the function :py:class:`~ddg.datastructures.nets.conversion.sample_smooth_net`. .. doctest:: >>> import ddg >>> snet = ddg.nets.net_generators.spheres_and_circles.circle((0, 0, 0), 1) >>> dnet = ddg.sample_smooth_net(snet, sampling=[0.5, 10, "c"]) .. seealso:: :ref:`Guide on sampling a SmoothNet `. The :py:class:`~ddg.datastructures.nets.net.DiscreteNet` can be visualized: .. doctest:: >>> blender_object = ddg.to_blender_object(dnet) .. seealso:: :ref:`Usage of to_blender_object `. General arguments of the function :py:func:`~ddg.conversion.blender.core.to_blender_object` can be found in its api documentation: :py:func:`~ddg.conversion.blender.core.to_blender_object`. Depending on the type of the DiscreteNet (:py:class:`~ddg.datastructures.nets.net.DiscreteNet`, :py:class:`~ddg.datastructures.nets.net.DiscreteCurve`, :py:class:`~ddg.datastructures.nets.net.PointNet`), additional arguments can be given to :py:func:`~ddg.conversion.blender.core.to_blender_object`: .. include:: _to_blender_object/docstring.txt :start-line: 57 :end-line: 84 Using to_blender_object_helper ------------------------------ The function :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` combines the following - easy handling of commonly used keyword arguments - conversion to objects that can be visualized - calling the function :py:func:`~ddg.conversion.blender.core.to_blender_object`. A :py:class:`~ddg.datastructures.nets.net.SmoothNet` can be handed directly to :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` together with a sampling. .. doctest:: >>> blender_object = ddg.to_blender_object_helper(snet, sampling=[0.5, 10, "c"]) .. seealso:: :ref:`Usage of to_blender_object_helper `. General arguments of the function :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` can be found in its api documentation: :py:func:`~ddg.conversion.blender.core.to_blender_object_helper`. Depending on the type of net additional arguments can be handed over or might be required: .. include:: _to_blender_object/docstring.txt :start-line: 188 :end-line: 221 Nets with depth need to be handled with care: .. include:: _to_blender_object/docstring.txt :start-line: 236 :end-line: 248 Examples -------- The general process for visualizing nets was outlined above. In this section, we give examples for some extra functionality. We start with bounding boxes: While in our library, DiscreteNets and DiscreteCurves are more or less the same thing, they result in different objects, Blender Meshes and Blender Curves, after conversion. In particular, mesh and bmesh transformations can not be applied to curves. To cut an object inside a bounding box, we need to use :py:func:`ddg.visualization.blender.bmesh.cut_bounding_box` for 2D :py:class:`~ddg.datastructures.nets.net.DiscreteNet` objects but :py:func:`ddg.datastructures.nets.utils.cut_bounding_box` for :py:class:`~ddg.datastructures.nets.net.DiscreteCurve` objects. These work quite differently: The first is applied after conversion to the Blender Mesh and the second to the DiscreteCurve, before conversion to Blender. Let's see an example for each of these cases. Let's start with curves: .. _Curve render: We will start with a simple helix curve. Here we simply define a function that parametrizes the helix and then form the curve using :py:class:`~ddg.datastructures.nets.net.SmoothCurve` (you can also use `SmoothNet`, which will return a `SmoothCurve` automatically). We convert this smooth curve to a :py:class:`~ddg.datastructures.nets.net.DiscreteNet` by using :py:func:`~ddg.datastructures.nets.conversion.sample_smooth_net`. The net is then converted to blender_object using :py:func:`~ddg.conversion.blender.core.to_blender_object`. .. doctest:: >>> import numpy as np >>> def helix_parametrization(t): ... return np.array([np.cos(t), np.sin(t), 0.1 * t]) ... >>> helix_smooth = ddg.SmoothCurve(helix_parametrization, [-np.inf, np.inf]) >>> helix_discrete = ddg.sample_smooth_net(helix_smooth, sampling=[0.1, 500, "c"]) >>> bobj = ddg.to_blender_object(helix_discrete) .. image:: net_visualization_example_curve_unbounded.png :align: center :width: 50% Right now, the length of the helix is determined by the `sampling` somehow, but an easier way to gain more control over this is to use :py:func:`ddg.datastructures.nets.utils.cut_bounding_box`. For example, we might only want to only see two turns of the helix: .. doctest:: >>> helix_bounded = ddg.datastructures.nets.utils.cut_bounding_box( ... helix_discrete, (np.inf, np.inf, 2 * 0.1 * np.pi) ... ) >>> bobj_bounded = ddg.to_blender_object(helix_bounded) The 3-tuple :math:`(a, b, c)` means that the curve will be intersected with the axis-parallel, centered at 0 cuboid :math:`[-a, a] \times [-b, b] \times [-c, c]`. We get the following picture: .. image:: net_visualization_example_curve_bounded.png :align: center :width: 50% .. _Net render: Let's do the same thing but with a surface instead of a curve. Sticking with the helical theme: .. doctest:: >>> def helicoid_parametrization(u, v): ... return np.array([u * np.cos(v), u * np.sin(v), 0.2 * v]) ... >>> helicoid_smooth = ddg.SmoothNet( ... helicoid_parametrization, [[-np.inf, np.inf], [-np.inf, np.inf]] ... ) >>> helicoid_discrete = ddg.sample_smooth_net( ... helicoid_smooth, sampling=[0.02, 300, "c"] ... ) >>> bobj = ddg.to_blender_object(helicoid_discrete) produces the following not so useful image: .. image:: net_visualization_example_surface_unbounded.png :align: center :width: 50% Let's bound the surface so we can actually see something. In the case of surfaces we have to use :py:func:`ddg.visualization.blender.bmesh.cut_bounding_box` as a bmesh transformation: .. doctest:: >>> import functools >>> bounding_box_trafo = functools.partial( ... ddg.visualization.blender.bmesh.cut_bounding_box, ... distances=np.array([0.8, 0.8, 2 * 0.2 * np.pi]), ... center=np.array([0, 0, -0.2]), ... ) >>> bobj_bounded = ddg.to_blender_object( ... helicoid_discrete, bmesh_transformations=[bounding_box_trafo] ... ) Doing this, we get this somewhat better image: .. image:: net_visualization_example_surface_bounded.png :align: center :width: 50% For convenience, we also have the helper function :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` to implicitly render :py:class:`~ddg.datastructures.nets.net.SmoothNet` (or :py:class:`~ddg.datastructures.nets.net.SmoothCurve`) directly, without converting it to :py:class:`~ddg.datastructures.nets.net.DiscreteNet` objects first. All the steps to :ref:`render the helix` can alternatively be done using :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` by doing the following: .. doctest:: >>> def helix_parametrization(t): ... return np.array([np.cos(t), np.sin(t), 0.1 * t]) ... >>> helix_smooth = ddg.SmoothCurve(helix_parametrization, [-np.inf, np.inf]) >>> bobj_bounded = ddg.to_blender_object_helper( ... helix_smooth, ... sampling=[0.1, 500, "c"], ... bounding_box=[np.inf, np.inf, 2 * 0.1 * np.pi], ... ) Additionally, :ref:`the helicoid` is analogously rendered by: .. doctest:: >>> def helicoid_parametrization(u, v): ... return np.array([u * np.cos(v), u * np.sin(v), 0.2 * v]) ... >>> helicoid_smooth = ddg.SmoothNet( ... helicoid_parametrization, [[-np.inf, np.inf], [-np.inf, np.inf]] ... ) >>> bobj_bounded = ddg.to_blender_object_helper( ... helicoid_smooth, ... sampling=[0.02, 300, "c"], ... bounding_box=[0.8, 0.8, 2 * 0.2 * np.pi], ... ) .. note:: For :py:class:`~ddg.datastructures.nets.net.SmoothNet`, :py:func:`~ddg.conversion.blender.core.to_blender_object_helper` only supports bounding box centered at :math:`(0,0,0)`.