From 05d2b22bdbaf44ef50ace49485e69d13c38b40d4 Mon Sep 17 00:00:00 2001 From: Andrew Maclean <andrew.amaclean@gmail.com> Date: Mon, 13 May 2024 13:31:32 +1000 Subject: [PATCH] Adding examples --- src/Cxx.md | 2 +- src/Python.md | 2 +- src/PythonicAPI.md | 9 +- src/PythonicAPI/Filtering/Glyph2D.md | 3 + src/PythonicAPI/Filtering/Glyph2D.py | 60 +++++ src/PythonicAPI/GeometricObjects/Planes.py | 238 ++++++++++++++++++ src/PythonicAPI/Graphs/GraphToPolyData.md | 3 + src/PythonicAPI/Graphs/GraphToPolyData.py | 70 ++++++ src/PythonicAPI/Graphs/ScaleVertices.md | 3 + src/PythonicAPI/Graphs/ScaleVertices.py | 77 ++++++ .../Graphs/VisualizeDirectedGraph.py | 106 ++++++++ src/PythonicAPI/Graphs/VisualizeGraph.md | 6 + src/PythonicAPI/Snippets/VTKDataClasses.md | 24 ++ .../PythonicAPI/Filtering/TestGlyph2D.png | 3 + .../GeometricObjects/TestPlanes.png | 3 + .../Graphs/TestGraphToPolyData.png | 3 + .../PythonicAPI/Graphs/TestScaleVertices.png | 3 + .../Graphs/TestVisualizeDirectedGraph.png | 3 + 18 files changed, 614 insertions(+), 4 deletions(-) create mode 100644 src/PythonicAPI/Filtering/Glyph2D.md create mode 100755 src/PythonicAPI/Filtering/Glyph2D.py create mode 100755 src/PythonicAPI/GeometricObjects/Planes.py create mode 100644 src/PythonicAPI/Graphs/GraphToPolyData.md create mode 100755 src/PythonicAPI/Graphs/GraphToPolyData.py create mode 100644 src/PythonicAPI/Graphs/ScaleVertices.md create mode 100755 src/PythonicAPI/Graphs/ScaleVertices.py create mode 100755 src/PythonicAPI/Graphs/VisualizeDirectedGraph.py create mode 100644 src/PythonicAPI/Graphs/VisualizeGraph.md create mode 100644 src/Testing/Baseline/PythonicAPI/Filtering/TestGlyph2D.png create mode 100644 src/Testing/Baseline/PythonicAPI/GeometricObjects/TestPlanes.png create mode 100644 src/Testing/Baseline/PythonicAPI/Graphs/TestGraphToPolyData.png create mode 100644 src/Testing/Baseline/PythonicAPI/Graphs/TestScaleVertices.png create mode 100644 src/Testing/Baseline/PythonicAPI/Graphs/TestVisualizeDirectedGraph.png diff --git a/src/Cxx.md b/src/Cxx.md index b85838e65a6..f8b89d00353 100644 --- a/src/Cxx.md +++ b/src/Cxx.md @@ -1096,7 +1096,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri [ProbeCombustor](/Cxx/VisualizationAlgorithms/ProbeCombustor) | Probing data in a combustor. Probes are regular arrays of 50 by 50 points that are then passed through a contouring filter. [ProgrammableGlyphFilter](/Cxx/Visualization/ProgrammableGlyphFilter) | Generate a custom glyph at each point. [ProteinRibbons](/Cxx/Visualization/ProteinRibbons) | Display pdb ribbons. -[PseudoVolumeRendering](/Cxx/VolumeRendering/PseudoVolumeRendering) | Here we use 100 cut planes, each with an opacity of 0.05. They are then rendered back-to-front to simulate volume rendering. +[PseudoVolumeRendering](/Cxx/VolumeRendering/PseudoVolumeRendering) | Here we use 20 cut planes, each with an opacity of of 0.25. They are then rendered back-to-front to simulate volume rendering. [QuadraticSurface](/Cxx/Visualization/QuadraticSurface) | Display a quadratic surface. [QuadricLODActor](/Cxx/Visualization/QuadricLODActor) | Level of detail adjustment. [QuadricVisualization](/Cxx/Visualization/QuadricVisualization) | Visualizing a quadric function. diff --git a/src/Python.md b/src/Python.md index ec8eb2ba3c3..d8ead4a5c2b 100644 --- a/src/Python.md +++ b/src/Python.md @@ -596,7 +596,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri [ProbeCombustor](/Python/VisualizationAlgorithms/ProbeCombustor) | Probing data in a combustor. Probes are regular arrays of 50 by 50 points that are then passed through a contouring filter. [ProgrammableGlyphFilter](/Python/Visualization/ProgrammableGlyphFilter) | Generate a custom glyph at each point. [ProgrammableGlyphs](/Python/Visualization/ProgrammableGlyphs) | Generate programmable glyphs. -[PseudoVolumeRendering](/Python/VolumeRendering/PseudoVolumeRendering) | Here we use 100 cut planes, each with an opacity of 0.05. They are then rendered back-to-front to simulate volume rendering. +[PseudoVolumeRendering](/Python/VolumeRendering/PseudoVolumeRendering) | Here we use 20 cut planes, each with an opacity of of 0.25. They are then rendered back-to-front to simulate volume rendering. [QuadricVisualization](/Python/Visualization/QuadricVisualization) | Visualizing a quadric function. [SingleSplat](/Python/VisualizationAlgorithms/SingleSplat) | Elliptical splatting. (a) Single elliptical splat with eccentricity E=10. Cone shows orientation of vector. [SphereTexture](/Python/Visualization/SphereTexture) | Apply an ImageData texture to an sphere diff --git a/src/PythonicAPI.md b/src/PythonicAPI.md index 764144d770b..5b16622e13c 100644 --- a/src/PythonicAPI.md +++ b/src/PythonicAPI.md @@ -96,6 +96,7 @@ This Python script, [SelectExamples](../PythonicAPI/Utilities/SelectExamples), w | -------------- | ------------- | ------- | [GeometricObjectsDemo](/PythonicAPI/GeometricObjects/GeometricObjectsDemo) | [PipelineReuse](/PythonicAPI/GeometricObjects/PipelineReuse) | How to reuse a pipeline. +[Planes](/PythonicAPI/GeometricObjects/Planes) | We create a convex hull of the planes for display purposes. [SourceObjectsDemo](/PythonicAPI/GeometricObjects/SourceObjectsDemo) | Examples of source objects that procedurally generate polygonal models. These nine images represent just some of the capability of VTK. From upper left in reading order: sphere, cone, cylinder, cube, plane, text, random point cloud, disk (with or without hole), and line source. Other polygonal source objects are available; check subclasses of vtkPolyDataAlgorithm. ### Cells @@ -155,6 +156,7 @@ This Python script, [SelectExamples](../PythonicAPI/Utilities/SelectExamples), w [ExtractSelectionCells](/PythonicAPI/PolyData/ExtractSelectionCells) | Extract cell, select cell. [Finance](/PythonicAPI/Modelling/Finance) | Visualization of multidimensional financial data. The gray/wireframe surface represents the total data population. The red surface represents data points delinquent on loan payment. [FinanceFieldData](/PythonicAPI/Modelling/FinanceFieldData) | Visualization of multidimensional financial data. The yellow surface represents the total data population. The red surface represents data points delinquent on loan payment. +[Glyph2D](/PythonicAPI/Filtering/Glyph2D) | [LineOnMesh](/PythonicAPI/DataManipulation/LineOnMesh) | Plot a spline on a terrain-like surface. [MeshLabelImageColor](/PythonicAPI/DataManipulation/MeshLabelImageColor) | Mesh a single label from a label image. Then smooth and color the vertices according to the displacement error introduced by the smoothing. [SmoothMeshGrid](/PythonicAPI/PolyData/SmoothMeshGrid) | Create a terrain with regularly spaced points and smooth it with ?vtkLoopSubdivisionFilter? and ?vtkButterflySubdivisionFilter?. @@ -265,8 +267,11 @@ This section includes ?vtkUnstructuredGrid?. | Example Name | Description | Image | | -------------- | ------------- | ------- | -[SideBySideGraphs](/PythonicAPI/Graphs/SideBySideGraphs) | Display two graphs side by side. +[GraphToPolyData](/PythonicAPI/Graphs/GraphToPolyData) | Convert a graph to a PolyData. [LabelVerticesAndEdges](/PythonicAPI/Graphs/LabelVerticesAndEdges) | Label vertices and edges. +[SideBySideGraphs](/PythonicAPI/Graphs/SideBySideGraphs) | Display two graphs side by side. +[ScaleVertices](/PythonicAPI/Graphs/ScaleVertices) | Size/scale vertices based on a data array. +[VisualizeDirectedGraph](/PythonicAPI/Graphs/VisualizeDirectedGraph) | Visualize a directed graph. ### Graph Conversions @@ -332,7 +337,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri [FlyingHeadSlice](/PythonicAPI/VisualizationAlgorithms/FlyingHeadSlice) | Flying edges used to generate contour lines. [DataSetSurface](/PythonicAPI/VisualizationAlgorithms/DataSetSurface) | Cutting a hexahedron with a plane. The red line on the surface shows the cut. [PointDataSubdivision](/PythonicAPI/Visualization/PointDataSubdivision) | Demonstrates the use of the vtkLinearSubdivisionFilter and vtkButterflySubdivisionFilter. -[PseudoVolumeRendering](/PythonicAPI/VolumeRendering/PseudoVolumeRendering) | Here we use 20 cut planes, each with an opacity of 0.05. They are then rendered back-to-front to simulate volume rendering. +[PseudoVolumeRendering](/PythonicAPI/VolumeRendering/PseudoVolumeRendering) | Here we use 20 cut planes, each with an opacity of of 0.25. They are then rendered back-to-front to simulate volume rendering. [QuadricVisualization](/PythonicAPI/Visualization/QuadricVisualization) | Visualizing a quadric function. [WarpCombustor](/PythonicAPI/VisualizationAlgorithms/WarpCombustor) | Carpet plots of combustor flow energy in a structured grid. Colors and plane displacement represent energy values. diff --git a/src/PythonicAPI/Filtering/Glyph2D.md b/src/PythonicAPI/Filtering/Glyph2D.md new file mode 100644 index 00000000000..b5dcf1cc255 --- /dev/null +++ b/src/PythonicAPI/Filtering/Glyph2D.md @@ -0,0 +1,3 @@ +### Description + +Copy a polydata to every point in the input set. We use a hexagon for the demo. diff --git a/src/PythonicAPI/Filtering/Glyph2D.py b/src/PythonicAPI/Filtering/Glyph2D.py new file mode 100755 index 00000000000..4a5528bfb27 --- /dev/null +++ b/src/PythonicAPI/Filtering/Glyph2D.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import vtkPoints +from vtkmodules.vtkCommonDataModel import vtkPolyData +from vtkmodules.vtkFiltersCore import vtkGlyph2D +from vtkmodules.vtkFiltersSources import vtkRegularPolygonSource +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkPolyDataMapper, + vtkRenderWindow, + vtkRenderWindowInteractor, + vtkRenderer +) + + +def main(): + colors = vtkNamedColors() + + points = vtkPoints() + points.InsertNextPoint(0, 0, 0) + points.InsertNextPoint(1, 1, 0) + points.InsertNextPoint(2, 2, 0) + + polydata = vtkPolyData() + polydata.SetPoints(points) + + # Create anything you want here, we will use a polygon for the demo. + polygon_source = vtkRegularPolygonSource() # default is 6 sides + + glyph_2d = vtkGlyph2D(input_data=polydata) + glyph_2d.SetSourceConnection(polygon_source.GetOutputPort()) + + mapper = vtkPolyDataMapper() + glyph_2d >> mapper + + actor = vtkActor(mapper=mapper) + actor.property.color = colors.GetColor3d('Salmon') + + # Visualize + renderer = vtkRenderer(background=colors.GetColor3d('SlateGray')) + render_window = vtkRenderWindow(window_name='Glyph2D') + render_window.AddRenderer(renderer) + render_window_interactor = vtkRenderWindowInteractor() + render_window_interactor.render_window = render_window + style = vtkInteractorStyleImage() + render_window_interactor.interactor_style = style + + renderer.AddActor(actor) + + render_window.Render() + render_window_interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/GeometricObjects/Planes.py b/src/PythonicAPI/GeometricObjects/Planes.py new file mode 100755 index 00000000000..643177d387d --- /dev/null +++ b/src/PythonicAPI/GeometricObjects/Planes.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingFreeType +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonDataModel import ( + vtkPlanes, + vtkPolyData +) +from vtkmodules.vtkFiltersCore import vtkHull +from vtkmodules.vtkFiltersSources import vtkSphereSource +from vtkmodules.vtkInteractionWidgets import ( + vtkTextRepresentation, + vtkTextWidget +) +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkCamera, + vtkPolyDataMapper, + vtkRenderWindow, + vtkRenderWindowInteractor, + vtkRenderer, + vtkTextActor, + vtkTextProperty +) + + +def main(): + colors = vtkNamedColors() + + planes = list() + titles = dict() + + # Using frustum planes. + titles[0] = 'Using frustum planes' + camera = vtkCamera() + planes_array = [0] * 24 + camera.GetFrustumPlanes(1, planes_array) + planes.append(vtkPlanes()) + planes[0].SetFrustumPlanes(planes_array) + + # Using bounds. + titles[1] = 'Using bounds' + sphere_source = vtkSphereSource() + sphere_source.update() + bounds = sphere_source.output.bounds + planes.append(vtkPlanes()) + planes[1].bounds = bounds + + # At this point we have the planes created by both of the methods above. + # You can do whatever you want with them. + + # For visualization, we will produce an n-sided convex hull + # and visualise it. + + ren_win = vtkRenderWindow(size=(600, 600), window_name='Planes') + + iren = vtkRenderWindowInteractor() + iren.render_window = ren_win + + actors = list() + renderers = list() + for i in range(0, len(planes)): + hull = vtkHull(planes=planes[i]) + pd = vtkPolyData() + + # To generate the convex hull we supply a vtkPolyData object and a bounding box. + # We define the bounding box to be where we expect the resulting polyhedron to lie. + # Make it a generous fit as it is only used to create the initial + # polygons that are eventually clipped. + hull.GenerateHull(pd, -200, 200, -200, 200, -200, 200) + + mapper = vtkPolyDataMapper(input_data=pd) + + actor = vtkActor(mapper=mapper) + actor.property.color = colors.GetColor3d('Moccasin') + actor.property.specular = 0.8 + actor.property.specular_power = 30 + + actors.append(actor) + renderers.append(vtkRenderer()) + renderers[i].AddActor(actors[i]) + + ren_win.AddRenderer(renderers[i]) + + # Define viewport ranges [x_min, y_min, x_max, y_max] + viewports = {0: [0.0, 0.0, 0.5, 1.0], + 1: [0.5, 0.0, 1.0, 1.0] + } + + # Set up the viewports. + x_grid_dimensions = 2 + y_grid_dimensions = 1 + renderer_size = 300 + ren_win.SetSize(renderer_size * x_grid_dimensions, renderer_size * y_grid_dimensions) + for row in range(0, y_grid_dimensions): + for col in range(0, x_grid_dimensions): + index = row * x_grid_dimensions + col + + if index > (len(renderers) - 1): + # Add a renderer even if there is no actor. + # This makes the render window background all the same color. + ren = vtkRenderer(background=colors.GetColor3d('DarkSlateGray'), viewport=viewports[index]) + ren_win.AddRenderer(ren) + continue + + renderers[index].SetViewport(viewports[index]) + renderers[index].SetBackground(colors.GetColor3d('DarkSlateGray')) + renderers[index].ResetCamera() + renderers[index].active_camera.Azimuth(30) + renderers[index].active_camera.Elevation(-30) + renderers[index].ResetCameraClippingRange() + + # Create the TextActors. + text_actors = list() + text_representations = list() + text_widgets = list() + text_property = vtkTextProperty(color=colors.GetColor3d('PeachPuff'), bold=False, italic=False, shadow=False, + font_size=12, + justification=TextProperty.Justification.VTK_TEXT_CENTERED, + vertical_justification=TextProperty.VerticalJustification.VTK_TEXT_CENTERED) + + text_positions = get_text_positions(list(titles.values()), + justification=TextProperty.Justification.VTK_TEXT_CENTERED, + vertical_justification=TextProperty.VerticalJustification.VTK_TEXT_BOTTOM, + width=0.7 + ) + + for k, v in titles.items(): + text_actors.append( + vtkTextActor(input=v, text_scale_mode=vtkTextActor.TEXT_SCALE_MODE_NONE, text_property=text_property)) + + # Create the text representation. Used for positioning the text actor. + text_representations.append(vtkTextRepresentation(enforce_normalized_viewport_bounds=True)) + text_representations[k].GetPositionCoordinate().value = text_positions[v]['p'] + text_representations[k].GetPosition2Coordinate().value = text_positions[v]['p2'] + + # Create the TextWidget + text_widgets.append( + vtkTextWidget(representation=text_representations[k], text_actor=text_actors[k], + default_renderer=renderers[k], interactor=iren, selectable=False)) + + iren.Initialize() + ren_win.Render() + + for k in titles.keys(): + text_widgets[k].On() + + iren.Start() + + +def get_text_positions(names, justification=0, vertical_justification=0, width=0.96, height=0.1): + """ + Get viewport positioning information for a list of names. + + :param names: The list of names. + :param justification: Horizontal justification of the text, default is left. + :param vertical_justification: Vertical justification of the text, default is bottom. + :param width: Width of the bounding_box of the text in screen coordinates. + :param height: Height of the bounding_box of the text in screen coordinates. + :return: A list of positioning information. + """ + # The gap between the left or right edge of the screen and the text. + dx = 0.02 + width = abs(width) + if width > 0.96: + width = 0.96 + + y0 = 0.01 + height = abs(height) + if height > 0.9: + height = 0.9 + dy = height + if vertical_justification == TextProperty.VerticalJustification.VTK_TEXT_TOP: + y0 = 1.0 - (dy + y0) + dy = height + if vertical_justification == TextProperty.VerticalJustification.VTK_TEXT_CENTERED: + y0 = 0.5 - (dy / 2.0 + y0) + dy = height + + name_len_min = 0 + name_len_max = 0 + first = True + for k in names: + sz = len(k) + if first: + name_len_min = name_len_max = sz + first = False + else: + name_len_min = min(name_len_min, sz) + name_len_max = max(name_len_max, sz) + text_positions = dict() + for k in names: + sz = len(k) + delta_sz = width * sz / name_len_max + if delta_sz > width: + delta_sz = width + + if justification == TextProperty.Justification.VTK_TEXT_CENTERED: + x0 = 0.5 - delta_sz / 2.0 + elif justification == TextProperty.Justification.VTK_TEXT_RIGHT: + x0 = 1.0 - dx - delta_sz + else: + # Default is left justification. + x0 = dx + + # For debugging! + # print( + # f'{k:16s}: (x0, y0) = ({x0:3.2f}, {y0:3.2f}), (x1, y1) = ({x0 + delta_sz:3.2f}, {y0 + dy:3.2f})' + # f', width={delta_sz:3.2f}, height={dy:3.2f}') + text_positions[k] = {'p': [x0, y0, 0], 'p2': [delta_sz, dy, 0]} + + return text_positions + + +@dataclass(frozen=True) +class TextProperty: + @dataclass(frozen=True) + class Justification: + VTK_TEXT_LEFT: int = 0 + VTK_TEXT_CENTERED: int = 1 + VTK_TEXT_RIGHT: int = 2 + + @dataclass(frozen=True) + class VerticalJustification: + VTK_TEXT_BOTTOM: int = 0 + VTK_TEXT_CENTERED: int = 1 + VTK_TEXT_TOP: int = 2 + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Graphs/GraphToPolyData.md b/src/PythonicAPI/Graphs/GraphToPolyData.md new file mode 100644 index 00000000000..c0e1d38ae7f --- /dev/null +++ b/src/PythonicAPI/Graphs/GraphToPolyData.md @@ -0,0 +1,3 @@ +### Description + +This example creates a simple graph and then converts it to a polydata for visualization using Paraview. diff --git a/src/PythonicAPI/Graphs/GraphToPolyData.py b/src/PythonicAPI/Graphs/GraphToPolyData.py new file mode 100755 index 00000000000..f25e9c3ce4a --- /dev/null +++ b/src/PythonicAPI/Graphs/GraphToPolyData.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import vtkPoints +from vtkmodules.vtkCommonDataModel import vtkMutableUndirectedGraph +from vtkmodules.vtkFiltersSources import vtkGraphToPolyData +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkPolyDataMapper, + vtkRenderWindow, + vtkRenderWindowInteractor, + vtkRenderer +) + + +def main(): + colors = vtkNamedColors() + # Create a graph. + g = vtkMutableUndirectedGraph() + + # Add 4 vertices to the graph. + v1 = g.AddVertex() + v2 = g.AddVertex() + v3 = g.AddVertex() + v4 = g.AddVertex() + + # Add 3 edges to the graph. + g.AddEdge(v1, v2) + g.AddEdge(v1, v3) + g.AddEdge(v1, v4) + + # Create 4 points - one for each vertex. + points = vtkPoints() + points.InsertNextPoint(0.0, 0.0, 0.0) + points.InsertNextPoint(1.0, 0.0, 0.0) + points.InsertNextPoint(0.0, 1.0, 0.0) + points.InsertNextPoint(0.0, 0.0, 1.0) + + # Add the coordinates of the points to the graph. + g.points = points + + # Convert the graph to a polydata. + graph_to_poly_data = vtkGraphToPolyData(input_data=g) + + # Create a mapper and actor. + mapper = vtkPolyDataMapper() + graph_to_poly_data >> mapper + actor = vtkActor(mapper=mapper) + + # Create a renderer, render window, and interactor. + renderer = vtkRenderer(background=colors.GetColor3d('Green')) + render_window = vtkRenderWindow(window_name='GraphToPolyData') + render_window.AddRenderer(renderer) + render_window_interactor = vtkRenderWindowInteractor() + render_window_interactor.render_window = render_window + + # Add the actor to the scene. + renderer.AddActor(actor) + + # Render and interact. + render_window.Render() + render_window_interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Graphs/ScaleVertices.md b/src/PythonicAPI/Graphs/ScaleVertices.md new file mode 100644 index 00000000000..523d70a9857 --- /dev/null +++ b/src/PythonicAPI/Graphs/ScaleVertices.md @@ -0,0 +1,3 @@ +### Description + +Scale the vertices based on a data array. diff --git a/src/PythonicAPI/Graphs/ScaleVertices.py b/src/PythonicAPI/Graphs/ScaleVertices.py new file mode 100755 index 00000000000..36d3eedd7fe --- /dev/null +++ b/src/PythonicAPI/Graphs/ScaleVertices.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import ( + vtkFloatArray, + vtkIntArray, + vtkLookupTable +) +from vtkmodules.vtkCommonDataModel import vtkMutableUndirectedGraph +#from vtkmodules.vtkInfovisLayout import vtkForceDirectedLayoutStrategy +from vtkmodules.vtkRenderingCore import vtkGraphToGlyphs +from vtkmodules.vtkViewsCore import vtkViewTheme +from vtkmodules.vtkViewsInfovis import ( + vtkGraphLayoutView, + vtkRenderedGraphRepresentation +) + + +def main(): + colors = vtkNamedColors() + + g = vtkMutableUndirectedGraph() + + v1 = g.AddVertex() + v2 = g.AddVertex() + + g.AddEdge(v1, v2) + g.AddEdge(v1, v2) + + scales = vtkFloatArray(number_of_components=1, name='Scales') + scales.InsertNextValue(2.0) + scales.InsertNextValue(5.0) + + # Add the scale array to the graph. + g.GetVertexData().AddArray(scales) + + # Create the color array + vertex_colors = vtkIntArray(number_of_components=1, name='Color') + vertex_colors.InsertNextValue(0) + vertex_colors.InsertNextValue(1) + + # Add the color array to the graph. + g.GetVertexData().AddArray(vertex_colors) + + lookup_table = vtkLookupTable(number_of_table_values=2) + lookup_table.SetTableValue(0, colors.GetColor4d('Yellow')) + lookup_table.SetTableValue(1, colors.GetColor4d('Lime')) + lookup_table.Build() + + theme = vtkViewTheme() + theme.SetPointLookupTable(lookup_table) + + # force_directed = vtkForceDirectedLayoutStrategy() + layout_view = vtkGraphLayoutView(scaled_glyphs=True, color_vertices=True, + scaling_array_name='Scales', vertex_color_array_name='Color') + # If we create a layout object directly, just set the pointer through this method. + # graph_layout_view.SetLayoutStrategy(force_directed) + layout_view.SetLayoutStrategyToForceDirected() + layout_view.AddRepresentationFromInput(g) + layout_view.ApplyViewTheme(theme) + r_graph = vtkRenderedGraphRepresentation() + g_glyph = vtkGraphToGlyphs() + r_graph.SafeDownCast(layout_view.GetRepresentation()).SetGlyphType(g_glyph.CIRCLE) + layout_view.renderer.background = colors.GetColor3d('Navy') + layout_view.renderer.background2 = colors.GetColor3d('MidnightBlue') + layout_view.render_window.window_name = 'ScaleVertices' + layout_view.Render() + layout_view.ResetCamera() + layout_view.interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Graphs/VisualizeDirectedGraph.py b/src/PythonicAPI/Graphs/VisualizeDirectedGraph.py new file mode 100755 index 00000000000..bb2156bbd6b --- /dev/null +++ b/src/PythonicAPI/Graphs/VisualizeDirectedGraph.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonDataModel import vtkMutableDirectedGraph +from vtkmodules.vtkFiltersCore import vtkGlyph3D +from vtkmodules.vtkFiltersSources import ( + vtkGlyphSource2D, + vtkGraphToPolyData +) +from vtkmodules.vtkInfovisLayout import ( + vtkGraphLayout, + vtkSimple2DLayoutStrategy +) +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkPolyDataMapper +) +from vtkmodules.vtkViewsInfovis import vtkGraphLayoutView +from vtkmodules.vtkCommonColor import vtkNamedColors + + +def main(): + + colors = vtkNamedColors() + + g = vtkMutableDirectedGraph() + + v1 = g.AddVertex() + v2 = g.AddVertex() + v3 = g.AddVertex() + + g.AddEdge(v1, v2) + g.AddEdge(v2, v3) + g.AddEdge(v3, v1) + + strategy = vtkSimple2DLayoutStrategy() + layout = vtkGraphLayout(input_data=g, layout_strategy=strategy) + + # Do layout manually before handing graph to the view. + # This allows us to know the positions of edge arrows. + graph_layout_view = vtkGraphLayoutView() + + # Tell the view to use the vertex layout we provide + graph_layout_view.SetLayoutStrategyToPassThrough() + # The arrows will be positioned on a straight line between two + # vertices so tell the view not to draw arcs for parallel edges + graph_layout_view.SetEdgeLayoutStrategyToPassThrough() + + # Add the graph to the view. This will render vertices and edges, + # but not edge arrows. + graph_layout_view.AddRepresentationFromInputConnection(layout.GetOutputPort()) + + # Manually create an actor containing the glyphed arrows. + # Set the position (0: edge start, 1: edge end) where + # the edge arrows should go. + graph_to_poly = vtkGraphToPolyData(edge_glyph_output=True, edge_glyph_position=0.98) + layout >> graph_to_poly + + # Make a simple edge arrow for glyphing. + arrow_source = vtkGlyphSource2D(scale=0.1, glyph_type=GlyphSource2D.GlyphType.VTK_EDGEARROW_GLYPH) + + # Use Glyph3D to repeat the glyph on all edges. + arrow_glyph = vtkGlyph3D() + arrow_glyph.SetInputConnection(0, graph_to_poly.GetOutputPort(1)) + arrow_glyph.SetInputConnection(1, arrow_source.GetOutputPort()) + + # Add the edge arrow actor to the view. + arrow_mapper = vtkPolyDataMapper() + arrow_glyph >> arrow_mapper + arrowActor = vtkActor(mapper=arrow_mapper) + + graph_layout_view.renderer.background = colors.GetColor3d('SaddleBrown') + graph_layout_view.renderer.background2 = colors.GetColor3d('Wheat') + graph_layout_view.renderer.AddActor(arrowActor) + + graph_layout_view.ResetCamera() + graph_layout_view.Render() + graph_layout_view.interactor.Start() + + +@dataclass(frozen=True) +class GlyphSource2D: + @dataclass(frozen=True) + class GlyphType: + VTK_NO_GLYPH: int = 0 + VTK_VERTEX_GLYPH: int = 1 + VTK_DASH_GLYPH: int = 2 + VTK_CROSS_GLYPH: int = 3 + VTK_THICKCROSS_GLYPH: int = 4 + VTK_TRIANGLE_GLYPH: int = 5 + VTK_SQUARE_GLYPH: int = 6 + VTK_CIRCLE_GLYPH: int = 7 + VTK_DIAMOND_GLYPH: int = 8 + VTK_ARROW_GLYPH: int = 9 + VTK_THICKARROW_GLYPH: int = 10 + VTK_HOOKEDARROW_GLYPH: int = 11 + VTK_EDGEARROW_GLYPH: int = 12 + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Graphs/VisualizeGraph.md b/src/PythonicAPI/Graphs/VisualizeGraph.md new file mode 100644 index 00000000000..3eec7502776 --- /dev/null +++ b/src/PythonicAPI/Graphs/VisualizeGraph.md @@ -0,0 +1,6 @@ +### Description + +* Contributed by Jim McCusker + +!!! note +AddEdge() is not enabled through python, but AddGraphEdge() is. diff --git a/src/PythonicAPI/Snippets/VTKDataClasses.md b/src/PythonicAPI/Snippets/VTKDataClasses.md index d38417de7e7..0f7c4a39a89 100644 --- a/src/PythonicAPI/Snippets/VTKDataClasses.md +++ b/src/PythonicAPI/Snippets/VTKDataClasses.md @@ -229,6 +229,30 @@ class Glyph3D: ``` +### GlyphSource2D + +``` Python +@dataclass(frozen=True) +class GlyphSource2D: + @dataclass(frozen=True) + class GlyphType: + VTK_NO_GLYPH: int = 0 + VTK_VERTEX_GLYPH: int = 1 + VTK_DASH_GLYPH: int = 2 + VTK_CROSS_GLYPH: int = 3 + VTK_THICKCROSS_GLYPH: int = 4 + VTK_TRIANGLE_GLYPH: int = 5 + VTK_SQUARE_GLYPH: int = 6 + VTK_CIRCLE_GLYPH: int = 7 + VTK_DIAMOND_GLYPH: int = 8 + VTK_ARROW_GLYPH: int = 9 + VTK_THICKARROW_GLYPH: int = 10 + VTK_HOOKEDARROW_GLYPH: int = 11 + VTK_EDGEARROW_GLYPH: int = 12 + + +``` + ### ImageCanvasSource2D ``` Python diff --git a/src/Testing/Baseline/PythonicAPI/Filtering/TestGlyph2D.png b/src/Testing/Baseline/PythonicAPI/Filtering/TestGlyph2D.png new file mode 100644 index 00000000000..f1d55b9cdb4 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Filtering/TestGlyph2D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66ea4971006cfd9ea557ddbb5deea0479c10e4d57a805085bdeeb5fffa3afc21 +size 2482 diff --git a/src/Testing/Baseline/PythonicAPI/GeometricObjects/TestPlanes.png b/src/Testing/Baseline/PythonicAPI/GeometricObjects/TestPlanes.png new file mode 100644 index 00000000000..dcee9284e25 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/GeometricObjects/TestPlanes.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21d0724667d1643c9bae9585b0f1241bc06aa0f27c6a88b091919f15271eb427 +size 11987 diff --git a/src/Testing/Baseline/PythonicAPI/Graphs/TestGraphToPolyData.png b/src/Testing/Baseline/PythonicAPI/Graphs/TestGraphToPolyData.png new file mode 100644 index 00000000000..c9691680c7a --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Graphs/TestGraphToPolyData.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7aacaa21b93f62f04d52dc90d0eac4c04e3444fb390abef6f688d1d4635098d +size 1081 diff --git a/src/Testing/Baseline/PythonicAPI/Graphs/TestScaleVertices.png b/src/Testing/Baseline/PythonicAPI/Graphs/TestScaleVertices.png new file mode 100644 index 00000000000..7c9612b5af2 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Graphs/TestScaleVertices.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:579ee9e46b30052d10bb89b2d844473133a712cb9d9ce031c9a01372c711ec58 +size 2820 diff --git a/src/Testing/Baseline/PythonicAPI/Graphs/TestVisualizeDirectedGraph.png b/src/Testing/Baseline/PythonicAPI/Graphs/TestVisualizeDirectedGraph.png new file mode 100644 index 00000000000..6a7b2709d81 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Graphs/TestVisualizeDirectedGraph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e329253171cc5fe5bbd0a8953f61c851ec858093e2e37a72bcdd57cb01a6c79 +size 58003 -- GitLab