{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Setting Backface Properties {#backface_props_example}\n\nBy default front and backface rendering uses the same properties. In\ncertain situations it can be useful to set different properties for\nbackfaces than for frontfaces.\n\nOne straightforward example is when a closed (or close enough) surface\nhas a different color on the inside. Note that the notion of \\\"inside\\\"\nand \\\"outside\\\" depend on the orientation of the surface normals:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import numpy as np\n\nimport pyvista as pv\nfrom pyvista import examples\n\nmesh = pv.ParametricEllipsoid(min_v=np.pi / 2 - 0.2, max_v=np.pi / 2 + 0.2)\n\n# create a shifted copy with flipped normals\nmesh_inside_out = mesh.translate((0, 0, 1), inplace=False)\nmesh_inside_out.compute_normals(flip_normals=True, inplace=True)\nmeshes = mesh + mesh_inside_out\n\nbackface_params = dict(color='orangered')\nmeshes.plot(color='aquamarine', backface_params=backface_params, smooth_shading=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "A more interesting use case is helping visualize the orientation of\ncomplex, self-intersecting surfaces. For instance\n`Catalan's minimal surface\n<pyvista.ParametricCatalanMinimal>`{.interpreted-text role=\"func\"} has a\ncomplex shape, and coloring the front and backfaces differently helps\nviewers comprehend the intricate structure of the surface. This example\nalso demonstrates use of the\n`backface_prop <pyvista.Actor.backface_prop>`{.interpreted-text\nrole=\"attr\"} property of the `pyvista.Actor`{.interpreted-text\nrole=\"class\"} class.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "catalan = pv.ParametricCatalanMinimal()\nplotter = pv.Plotter()\nactor = plotter.add_mesh(catalan, color='dodgerblue', smooth_shading=True)\nbprop = actor.backface_prop\nbprop.color = 'forestgreen'\nbprop.specular = 1.0\nbprop.specular_power = 50.0\nplotter.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "In the case of non-orientable surfaces, adding specific backface\nproperties can make the non-orientable quality very obvious by the\nemergence of \\\"seams\\\" where the face properties are discontinuous.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "henneberg = pv.ParametricHenneberg().scale(0.25, inplace=False)\nklein = pv.ParametricKlein().rotate_z(150, inplace=False).translate((6, 0, 0), inplace=False)\nmeshes = henneberg + klein\n\nbackface_params = dict(color='mediumseagreen', specular=1.0, specular_power=50.0)\nmeshes.plot(color='gold', backface_params=backface_params, smooth_shading=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Of course we aren\\'t constrained to only setting distinct colors for\nbackfaces; most `pyvista.Property`{.interpreted-text role=\"class\"}\nattributes can be overridden. However, some of these have no effect,\nwhile others merely don\\'t make any sense. For instance, most objects\nhave the same opacity no matter which direction you look at them. Here\nis a GIF animation circling around such an asymmetrically opaque M\u00f6bius\nstrip:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "mobius = pv.ParametricMobius().rotate_z(-90, inplace=False)\nbackface_params = dict(opacity=0.5)\nplotter = pv.Plotter()\nplotter.add_mesh(mobius, color='deepskyblue', backface_params=backface_params, smooth_shading=True)\nplotter.open_gif('mobius_semiopaque.gif')\n\nviewup = [0, 0, 1]\norbit = plotter.generate_orbital_path(n_points=24, shift=0.0, viewup=viewup)\nplotter.orbit_on_path(orbit, write_frames=True, viewup=viewup, step=0.02)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Apply Backface Properties to Textured Meshes\n\nBackface textures can also be applied to meshes that have textures\napplied to them. For this example we load the globe texture with\n`pyvista.examples.load_globe() <pyvista.examples.examples.load_globe>`{.interpreted-text\nrole=\"func\"}, clip it, and then apply a different color to the interior\nsurface.\n\nThe lighting has been disabled for this example to demonstrate how you\ncan make the interior of the surface appear occluded without any\ndirectional lighting simply by providing a different color for backface.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "globe = examples.load_globe()\ntexture = examples.load_globe_texture()\nclipped = globe.clip(normal='z', value=4.37e9)\n\npl = pv.Plotter()\npl.add_mesh(\n    clipped,\n    backface_params={'color': [0.2, 0.2, 0.2]},\n    lighting=False,\n    smooth_shading=True,\n    texture=texture,\n)\npl.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Backface Properties and Physically Based Rendering\n\nNote that backfaces are automatically culled when physically based\nrendering is enabled, regardless of the settings of backface parameters.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "sphere = pv.Sphere()\nclipped_sphere = sphere.clip(normal='z', value=0.4)\n\npl = pv.Plotter()\npl.set_environment_texture(examples.download_sky_box_cube_map())\npl.add_mesh(\n    clipped_sphere,\n    backface_params={'color': 'r'},\n    pbr=True,\n    metallic=1.0,\n    roughness=0.2,\n)\npl.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "See also the `sphere_eversion_example`{.interpreted-text role=\"ref\"}\nexample which relies on distinguishing the inside and the outside of a\nsphere.\n"
      ]
    }
  ],
  "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.12.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}