Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TriMesh element #2143

Merged
merged 28 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
432ab07
Added TriMesh element
philippjfr Nov 22, 2017
c1e60ad
Simplified TriMesh path generation
philippjfr Nov 22, 2017
c095010
Declared TriMesh.group constant
philippjfr Nov 22, 2017
75b3676
Simplified TriMesh edgepaths code
philippjfr Nov 23, 2017
21451e6
Improvements for TriMesh
philippjfr Nov 24, 2017
63067ce
Added support for filled TriMesh simplices
philippjfr Nov 24, 2017
c6e9146
Defined default TriMesh styling
philippjfr Nov 24, 2017
3f61cca
Small fix for TriMesh path computation
philippjfr Nov 24, 2017
bcbb7bf
Added TriMesh reference entries
philippjfr Nov 24, 2017
bdbb3cc
Added tests and small fixes for TriMesh element
philippjfr Nov 24, 2017
58b98b0
Various TriMesh improvements
philippjfr Nov 28, 2017
2dcbc26
Added trimesh rasterization and generalized API
philippjfr Nov 28, 2017
d16a637
Split matplotlib paths before displaying them
philippjfr Dec 3, 2017
d5a6ea6
Optimized static bokeh Graph plots
philippjfr Dec 3, 2017
3f2a28d
Fixed various Graph edgepaths bugs
philippjfr Dec 3, 2017
d8cfd66
Restored default datashading aggregator
philippjfr Dec 3, 2017
1c9585d
Updated unit test
philippjfr Dec 3, 2017
9f9b569
Improved datashader dimension lookup
philippjfr Dec 7, 2017
b7f04ae
Added interpolation support for trimesh rasterization
philippjfr Dec 7, 2017
635a8ce
Improved datashader operations
philippjfr Dec 8, 2017
cf0d3c4
Improvements for bokeh GraphPlot
philippjfr Dec 8, 2017
2243bf6
Cleaned up datashader class hierarchy
philippjfr Dec 8, 2017
586a506
Handled empty TriMesh
philippjfr Dec 9, 2017
3480316
Addressed various TriMesh review comments
philippjfr Dec 14, 2017
ad2fcca
Fixed Dataset.add_dimension dtype bug
philippjfr Dec 14, 2017
74f961a
Fixed datashade operation docstrings
philippjfr Dec 14, 2017
65a44bd
Added vertex averaging to TriMesh plots
philippjfr Dec 14, 2017
ddf38cd
Revised TriMesh reference notebooks
philippjfr Dec 14, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions examples/reference/elements/bokeh/TriMesh.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
"<dl class=\"dl-horizontal\">\n",
" <dt>Title</dt> <dd> TriMesh Element</dd>\n",
" <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
" <dt>Backends</dt> <dd><a href='./TriMesh.ipynb'>Bokeh</a></dd> <dd><a href='../matplotlib/TriMesh.ipynb'>Matplotlib</a></dd>\n",
"</dl>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import holoviews as hv\n",
"from scipy.spatial import Delaunay\n",
"\n",
"hv.extension('bokeh')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``TriMesh`` represents a mesh of triangles represented as the simplexes and vertexes. The simplexes represent the indices into the vertex data, made up of three indices per triangle. The mesh therefore follows a datastructure very similar to a graph, with the abstract connectivity between nodes stored on the ``TriMesh`` element itself, the node or vertex positions stored on a ``Nodes`` element and the concrete ``EdgePaths`` making up each triangle generated when required by accessing the edgepaths attribute.\n",
"\n",
"Unlike a Graph each simplex is represented as the node indices of the three corners of each triangle rather than the usual source and target node.\n",
"\n",
"We will begin with a simple random mesh, generated by sampling some random integers and then applying Delaunay triangulation, which is available in SciPy. We can then construct the ``TriMesh`` by passing it the **simplexes** and the **vertices** (or **nodes**)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n_verts = 100\n",
"pts = np.random.randint(1, n_verts, (n_verts, 2))\n",
"tris = Delaunay(pts)\n",
"\n",
"trimesh = hv.TriMesh((tris.simplices, pts))\n",
"trimesh"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To make this easier TriMesh also provides a convenient ``from_vertices`` method, which will apply the Delaunay triangulation and construct the ``TriMesh`` for us:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hv.TriMesh.from_vertices(np.random.randn(100, 2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Just like the ``Graph`` element we can access the ``Nodes`` and ``EdgePaths`` via the ``.nodes`` and ``.edgepaths`` attributes respectively."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trimesh.nodes + trimesh.edgepaths"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's make a slightly more interesting example by generating a more complex geometry. Here we will compute a geometry, then apply Delaunay triangulation again and finally apply a mask to drop nodes in the center."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First create the x and y coordinates of the points.\n",
"n_angles = 36\n",
"n_radii = 8\n",
"min_radius = 0.25\n",
"radii = np.linspace(min_radius, 0.95, n_radii)\n",
"\n",
"angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)\n",
"angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)\n",
"angles[:, 1::2] += np.pi/n_angles\n",
"\n",
"x = (radii*np.cos(angles)).flatten()\n",
"y = (radii*np.sin(angles)).flatten()\n",
"z = (np.cos(radii)*np.cos(angles*3.0)).flatten()\n",
"nodes = np.column_stack([x, y, z])\n",
"\n",
"# Apply Delaunay triangulation\n",
"delauney = Delaunay(np.column_stack([x, y]))\n",
"\n",
"# Mask off unwanted triangles.\n",
"xmid = x[delauney.simplices].mean(axis=1)\n",
"ymid = y[delauney.simplices].mean(axis=1)\n",
"mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)\n",
"simplices = delauney.simplices[np.logical_not(mask)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Once again we can simply supply the simplices and nodes to the ``TriMesh``."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nodes = hv.Points(nodes, vdims='z')\n",
"hv.TriMesh((simplices, nodes))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also do something more interesting, e.g. by adding a value dimension to the vertices and coloring the edges by the vertex averaged value using the ``edge_color_index`` plot option:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%opts TriMesh [filled=True edge_color_index='z' width=400 height=400 tools=['hover'] inspection_policy='edges'] (cmap='viridis')\n",
"hv.TriMesh((simplices, nodes))"
]
}
],
"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.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
180 changes: 180 additions & 0 deletions examples/reference/elements/matplotlib/TriMesh.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"contentcontainer med left\" style=\"margin-left: -50px;\">\n",
"<dl class=\"dl-horizontal\">\n",
" <dt>Title</dt> <dd> TriMesh Element</dd>\n",
" <dt>Dependencies</dt> <dd>Bokeh</dd>\n",
" <dt>Backends</dt> <dd><a href='./TriMesh.ipynb'>Matplotlib</a></dd> <dd><a href='../bokeh/TriMesh.ipynb'>Bokeh</a></dd>\n",
"</dl>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import holoviews as hv\n",
"from scipy.spatial import Delaunay\n",
"\n",
"hv.extension('matplotlib')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``TriMesh`` represents a mesh of triangles represented as the simplexes and vertexes. The simplexes represent the indices into the vertex data, made up of three indices per triangle. The mesh therefore follows a datastructure very similar to a graph, with the abstract connectivity between nodes stored on the ``TriMesh`` element itself, the node or vertex positions stored on a ``Nodes`` element and the concrete ``EdgePaths`` making up each triangle generated when required by accessing the edgepaths attribute.\n",
"\n",
"Unlike a Graph each simplex is represented as the node indices of the three corners of each triangle rather than the usual source and target node.\n",
"\n",
"We will begin with a simple random mesh, generated by sampling some random integers and then applying Delaunay triangulation, which is available in SciPy. We can then construct the ``TriMesh`` by passing it the **simplexes** and the **vertices** (or **nodes**)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n_verts = 100\n",
"pts = np.random.randint(1, n_verts, (n_verts, 2))\n",
"tris = Delaunay(pts)\n",
"\n",
"trimesh = hv.TriMesh((tris.simplices, pts))\n",
"trimesh"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To make this easier TriMesh also provides a convenient ``from_vertices`` method, which will apply the Delaunay triangulation and construct the ``TriMesh`` for us:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hv.TriMesh.from_vertices(np.random.randn(100, 2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Just like the ``Graph`` element we can access the ``Nodes`` and ``EdgePaths`` via the ``.nodes`` and ``.edgepaths`` attributes respectively."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"trimesh.nodes + trimesh.edgepaths"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's make a slightly more interesting example by generating a more complex geometry. Here we will compute a geometry, then apply Delaunay triangulation again and finally apply a mask to drop nodes in the center."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First create the x and y coordinates of the points.\n",
"n_angles = 36\n",
"n_radii = 8\n",
"min_radius = 0.25\n",
"radii = np.linspace(min_radius, 0.95, n_radii)\n",
"\n",
"angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)\n",
"angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)\n",
"angles[:, 1::2] += np.pi/n_angles\n",
"\n",
"x = (radii*np.cos(angles)).flatten()\n",
"y = (radii*np.sin(angles)).flatten()\n",
"z = (np.cos(radii)*np.cos(angles*3.0)).flatten()\n",
"nodes = np.column_stack([x, y, z])\n",
"\n",
"# Apply Delaunay triangulation\n",
"delauney = Delaunay(np.column_stack([x, y]))\n",
"\n",
"# Mask off unwanted triangles.\n",
"xmid = x[delauney.simplices].mean(axis=1)\n",
"ymid = y[delauney.simplices].mean(axis=1)\n",
"mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)\n",
"simplices = delauney.simplices[np.logical_not(mask)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Once again we can simply supply the simplices and nodes to the ``TriMesh``."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hv.TriMesh((simplices, nodes))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also do something more interesting, e.g. by adding a value dimension to the vertices and coloring the edges by the vertex averaged value using the ``edge_color_index`` plot option:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%opts TriMesh [filled=True edge_color_index='z' fig_size=200] (cmap='viridis')\n",
"hv.TriMesh((simplices, hv.Points(nodes, vdims='z')))"
]
}
],
"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.6.3"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
6 changes: 5 additions & 1 deletion holoviews/core/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,11 @@ def add_dimension(self, dimension, dim_pos, dim_val, vdim=False, **kwargs):
dims.insert(dim_pos, dimension)
dimensions = dict(kdims=dims)

data = self.interface.add_dimension(self, dimension, dim_pos, dim_val, vdim)
if issubclass(self.interface, ArrayInterface) and np.asarray(dim_val).dtype != self.data.dtype:
element = self.clone(datatype=['pandas', 'dictionary'])
data = element.interface.add_dimension(element, dimension, dim_pos, dim_val, vdim)
else:
data = self.interface.add_dimension(self, dimension, dim_pos, dim_val, vdim)
return self.clone(data, **dimensions)


Expand Down
Loading