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