{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "%matplotlib inline"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Creating a Structured Surface {#create_structured}\n\nCreate a StructuredGrid surface from NumPy arrays using\n`pyvista.StructuredGrid`{.interpreted-text role=\"class\"}.\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"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# From NumPy Meshgrid\n\nCreate a simple meshgrid using NumPy\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Make data\nx = np.arange(-10, 10, 0.25)\ny = np.arange(-10, 10, 0.25)\nx, y = np.meshgrid(x, y)\nr = np.sqrt(x**2 + y**2)\nz = np.sin(r)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now pass the NumPy meshgrid to PyVista\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Create and plot structured grid\ngrid = pv.StructuredGrid(x, y, z)\ngrid.plot()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Plot mean curvature as well\ngrid.plot_curvature(clim=[-1, 1])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Generating a structured grid is a one-liner in this module, and the\npoints from the resulting surface can be accessed as a NumPy array:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "grid.points"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# From XYZ Points\n\nQuite often, you might be given a set of coordinates (XYZ points) in a\nsimple tabular format where there exists some structure such that grid\ncould be built between the nodes you have. A great example is found in\n[pyvista-support#16](https://github.com/pyvista/pyvista-support/issues/16)\nwhere a structured grid that is rotated from the cartesian reference\nframe is given as just XYZ points. In these cases, all that is needed to\nrecover the grid is the dimensions of the grid ([nx]{.title-ref} by\n[ny]{.title-ref} by [nz]{.title-ref}) and that the coordinates are\nordered appropriately.\n\nFor this example, we will create a small dataset and rotate the\ncoordinates such that they are not on orthogonal to cartesian reference\nframe.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "def make_point_set():\n    \"\"\"Ignore the contents of this function. Just know that it returns an\n    n by 3 numpy array of structured coordinates.\"\"\"\n    n, m = 29, 32\n    x = np.linspace(-200, 200, num=n) + np.random.default_rng().uniform(-5, 5, size=n)\n    y = np.linspace(-200, 200, num=m) + np.random.default_rng().uniform(-5, 5, size=m)\n    xx, yy = np.meshgrid(x, y)\n    A, b = 100, 100\n    zz = A * np.exp(-0.5 * ((xx / b) ** 2.0 + (yy / b) ** 2.0))\n    points = np.c_[xx.reshape(-1), yy.reshape(-1), zz.reshape(-1)]\n    foo = pv.PolyData(points)\n    foo.rotate_z(36.6, inplace=True)\n    return foo.points\n\n\n# Get the points as a 2D NumPy array (N by 3)\npoints = make_point_set()\npoints[0:5, :]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now pretend that the (n by 3) NumPy array above are coordinates that you\nhave, possibly from a file with three columns of XYZ points.\n\nWe simply need to recover the dimensions of the grid that these points\nmake and then we can generate a\n`pyvista.StructuredGrid`{.interpreted-text role=\"class\"} mesh.\n\nLet\\'s preview the points to see what we are dealing with:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n\nplt.figure(figsize=(10, 10))\nplt.scatter(points[:, 0], points[:, 1], c=points[:, 2])\nplt.axis(\"image\")\nplt.xlabel(\"X Coordinate\")\nplt.ylabel(\"Y Coordinate\")\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "In the figure above, we can see some inherit structure to the points and\nthus we could connect the points as a structured grid. All we need to\nknow are the dimensions of the grid present. In this case, we know\n(because we made this dataset) the dimensions are `[29, 32, 1]`, but you\nmight not know the dimensions of your pointset. There are a few ways to\nfigure out the dimensionality of structured grid including:\n\n-   manually counting the nodes along the edges of the pointset\n-   using a technique like principle component analysis to strip the\n    rotation from the dataset and count the unique values along each\n    axis for the new;y projected dataset.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# Once you've figured out your grid's dimensions, simple create the\n# :class:`pyvista.StructuredGrid` as follows:\n\nmesh = pv.StructuredGrid()\n# Set the coordinates from the numpy array\nmesh.points = points\n# set the dimensions\nmesh.dimensions = [29, 32, 1]\n\n# and then inspect it\nmesh.plot(show_edges=True, show_grid=True, cpos=\"xy\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Extending a 2D StructuredGrid to 3D\n\nA 2D `pyvista.StructuredGrid`{.interpreted-text role=\"class\"} mesh can\nbe extended into a 3D mesh. This is highly applicable when wanting to\ncreate a terrain following mesh in earth science research applications.\n\nFor example, we could have a `pyvista.StructuredGrid`{.interpreted-text\nrole=\"class\"} of a topography surface and extend that surface to a few\ndifferent levels and connect each \\\"level\\\" to create the 3D terrain\nfollowing mesh.\n\nLet\\'s start with a simple example by extending the wave mesh to 3D\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "struct = examples.load_structured()\nstruct.plot(show_edges=True)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "top = struct.points.copy()\nbottom = struct.points.copy()\nbottom[:, -1] = -10.0  # Wherever you want the plane\n\nvol = pv.StructuredGrid()\nvol.points = np.vstack((top, bottom))\nvol.dimensions = [*struct.dimensions[0:2], 2]\nvol.plot(show_edges=True)"
      ]
    }
  ],
  "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
}