{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Create Point Cloud {#create_point_cloud}\n\nCreate a `pyvista.PolyData`{.interpreted-text role=\"class\"} object from\na point cloud of vertices and scalar arrays for those points.\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": [ "Point clouds are generally constructed in the\n`pyvista.PolyData`{.interpreted-text role=\"class\"} class and can easily\nhave scalar/vector data arrays associated with the point cloud. In this\nexample, we\\'ll work a bit backwards using a point cloud that that is\navailable from our `examples` module. This however is no different than\ncreating a PyVista mesh with your own NumPy arrays of vertice locations.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Define some helpers - ignore these and use your own data.\ndef generate_points(subset=0.02):\n \"\"\"A helper to make a 3D NumPy array of points (n_points by 3)\"\"\"\n dataset = examples.download_lidar()\n ids = np.random.default_rng().integers(\n low=0,\n high=dataset.n_points - 1,\n size=int(dataset.n_points * subset),\n )\n return dataset.points[ids]\n\n\npoints = generate_points()\n# Print first 5 rows to prove its a numpy array (n_points by 3)\n# Columns are (X Y Z)\npoints[0:5, :]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that you have a NumPy array of points/vertices either from our\nsample data or your own project, creating a PyVista mesh of those points\nis simply:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "point_cloud = pv.PolyData(points)\npoint_cloud" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can even do a sanity check\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "np.allclose(points, point_cloud.points)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now that we have a PyVista mesh, we can plot it. Note that we add an\noption to use eye dome lighting - this is a shading technique to improve\ndepth perception with point clouds (learn more in\n`edl`{.interpreted-text role=\"ref\"}).\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "point_cloud.plot(eye_dome_lighting=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now what if you have data attributes (scalar/vector arrays) that you\\'d\nlike to associate with every node of your mesh? You can easily add NumPy\ndata arrays that have a length equal to the number of points in the mesh\nalong the first axis. For example, lets add a few arrays to this new\n`point_cloud` mesh.\n\nMake an array of scalar values with the same length as the points array.\nEach element in this array will correspond to points at the same index:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Make data array using z-component of points array\ndata = points[:, -1]\n\n# Add that data to the mesh with the name \"uniform dist\"\npoint_cloud[\"elevation\"] = data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now we can plot the point cloud with that random data. PyVista is\nsmart enough to plot the scalar array you added by default. Note that\nthis time, we specify to render every point as its own sphere.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "point_cloud.plot(render_points_as_spheres=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That data is kind of boring, right? You can also add data arrays with\nmore than one scalar value - perhaps a vector with three elements?\nLet\\'s make a little function that will compute vectors for every node\nin the point cloud and add those vectors to the mesh.\n\nThis time, we\\'re going to create a totally new, random point cloud.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Create random XYZ points\npoints = np.random.default_rng().random((100, 3))\n# Make PolyData\npoint_cloud = pv.PolyData(points)\n\n\ndef compute_vectors(mesh):\n origin = mesh.center\n vectors = mesh.points - origin\n vectors = vectors / np.linalg.norm(vectors, axis=1)[:, None]\n return vectors\n\n\nvectors = compute_vectors(point_cloud)\nvectors[0:5, :]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "point_cloud['vectors'] = vectors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can make arrows using those vectors using the glyph filter (see\n`glyph_example`{.interpreted-text role=\"ref\"} for more details).\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "arrows = point_cloud.glyph(\n orient='vectors',\n scale=False,\n factor=0.15,\n)\n\n# Display the arrows\nplotter = pv.Plotter()\nplotter.add_mesh(point_cloud, color='maroon', point_size=10.0, render_points_as_spheres=True)\nplotter.add_mesh(arrows, color='lightblue')\n# plotter.add_point_labels([point_cloud.center,], ['Center',],\n# point_color='yellow', point_size=20)\nplotter.show_grid()\nplotter.show()" ] } ], "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 }