diff --git a/src/PythonicAPI.md b/src/PythonicAPI.md index f372d366ccce6b04748a5af4bdf34580814ba919..5fd21496c19c8bf15ddb7058e17662cc69e0d3ed 100644 --- a/src/PythonicAPI.md +++ b/src/PythonicAPI.md @@ -300,6 +300,12 @@ This section includes ?vtkUnstructuredGrid?. ### Arrays +| Example Name | Description | Image | +| -------------- | ------------- | ------- | +[ArrayToTable](/PythonicAPI/InfoVis/ArrayToTable) | Convert a vtkDenseArray to a vtkTable. +[ArrayWriter](/PythonicAPI/Utilities/ArrayWriter) | Write a DenseArray or SparseArray to a file. + + ### Events ## Math Operations @@ -308,6 +314,7 @@ This section includes ?vtkUnstructuredGrid?. | Example Name | Description | Image | | -------------- | ------------- | ------- | +[AdjacencyMatrixToEdgeTable](/PythonicAPI/Graphs/AdjacencyMatrixToEdgeTable) | Convert an adjacency matrix to an edge table. [ConstructTree](/PythonicAPI/Graphs/ConstructTree) | Construct a tree. [CreateTree](/PythonicAPI/Graphs/CreateTree) | Create a tree and label the vertices and edges. [GraphToPolyData](/PythonicAPI/Graphs/GraphToPolyData) | Convert a graph to a PolyData. @@ -477,6 +484,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri | Example Name | Description | Image | | -------------- | ------------- | ------- | [BalloonWidget](/PythonicAPI/Widgets/BalloonWidget) | Uses a vtkBalloonWidget to draw labels when the mouse stays above an actor. +[BorderWidget](/PythonicAPI/Widgets/BorderWidget) | 2D selection, 2D box. [BoxWidget](/PythonicAPI/Widgets/BoxWidget) | This 3D widget defines a region of interest that is represented by an arbitrarily oriented hexahedron with interior face angles of 90 degrees (orthogonal faces). The object creates 7 handles that can be moused on and manipulated. [BoxWidget2](/PythonicAPI/Widgets/BoxWidget2) | This 3D widget defines a region of interest that is represented by an arbitrarily oriented hexahedron with interior face angles of 90 degrees (orthogonal faces). The object creates 7 handles that can be moused on and manipulated. ?vtkBoxWidget2? and ?vtkBoxRepresentation? are used in this example. [CompassWidget](/PythonicAPI/Widgets/CompassWidget) | Draws an interactive compass. diff --git a/src/PythonicAPI/Graphs/AdjacencyMatrixToEdgeTable.py b/src/PythonicAPI/Graphs/AdjacencyMatrixToEdgeTable.py new file mode 100644 index 0000000000000000000000000000000000000000..8bed799ff51c9e7b09c7cdf646102fe1d9ebab81 --- /dev/null +++ b/src/PythonicAPI/Graphs/AdjacencyMatrixToEdgeTable.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +""" +The output is: + + 10 20 30 + 40 50 60 + 70 80 90 + ++-----------------+------------------+ +| | value | ++-----------------+------------------+ +| 2 | 30 | +| 1 | 20 | +| 0 | 10 | +| 2 | 60 | +| 1 | 50 | +| 0 | 40 | +| 2 | 90 | +| 1 | 80 | +| 0 | 70 | ++-----------------+------------------+ + +The first column is the column index of the item in the 'value' column. +The row index is given by the number of times we've previously seen the column +index. For some reason, zeros in the matrix are not reported in the table. + +For example, the first row says that the value '30' is in column 2 of the matrix +(0-based indexing). Since we have not previously seen an item in column 2, it is +in row 0 of the matrix. + +The fourth row says that the value '60' is also in column 2. We infer that '60' +is row 1 of the matrix because we have already seen one item (the '30') in +column 2. + +""" + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonCore import vtkDenseArray +from vtkmodules.vtkCommonDataModel import vtkArrayData +from vtkmodules.vtkInfovisCore import ( + vtkAdjacencyMatrixToEdgeTable, + vtkArrayToTable +) + + +def main(): + # This is a templated class, note the use of square brackets for the template arguments. + array = vtkDenseArray['float']() + array.Resize(3, 3) + + counter = 1 + scale = 10 + for i in range(0, array.extents[0].GetEnd()): + for j in range(0, array.extents[1].GetEnd()): + array.SetValue(i, j, counter * scale) + counter += 1 + + array_data = vtkArrayData() + array_data.AddArray(array) + + # Optional step to check what we entered. + table = vtkArrayToTable(input_data=array_data) + table.update() + table.output.Dump() + + adjacency_matrix_to_edge_table = vtkAdjacencyMatrixToEdgeTable(input_data=array_data) + adjacency_matrix_to_edge_table.update() + adjacency_matrix_to_edge_table.output.Dump() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/InfoVis/ArrayToTable.py b/src/PythonicAPI/InfoVis/ArrayToTable.py new file mode 100644 index 0000000000000000000000000000000000000000..512da3618e623b91f6ca522779473438a85a1704 --- /dev/null +++ b/src/PythonicAPI/InfoVis/ArrayToTable.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonCore import vtkDenseArray +from vtkmodules.vtkCommonDataModel import vtkArrayData +from vtkmodules.vtkInfovisCore import ( + vtkArrayToTable +) + + +def main(): + # This is a templated class, note the use of square brackets for the template arguments. + array = vtkDenseArray['int']() + array.Resize(2, 4) + + print(f'The extents are are: ({array.extents[0].GetEnd()}, {array.extents[1].GetEnd()})') + # Set the values. + for i in range(0, array.extents[0].GetEnd()): + for j in range(0, array.extents[1].GetEnd()): + array.SetValue(i, j, i + j) + + array_data = vtkArrayData() + array_data.AddArray(array) + + table = vtkArrayToTable(input_data=array_data) + table.update() + table.output.Dump() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Snippets/VTKDataClasses.md b/src/PythonicAPI/Snippets/VTKDataClasses.md index e2a33b19576c1339378a1b03b8f87ab60026d455..9752d7f6f94f9499417d574cd908cf95b26f70f3 100644 --- a/src/PythonicAPI/Snippets/VTKDataClasses.md +++ b/src/PythonicAPI/Snippets/VTKDataClasses.md @@ -449,6 +449,20 @@ class Mapper: VTK_SCALAR_MODE_USE_CELL_FIELD_DATA: int = 4 VTK_SCALAR_MODE_USE_FIELD_DATA: int = 5 +``` +### PlatonicSolidSource + +``` Python +@dataclass(frozen=True) +class PlatonicSolidSource: + @dataclass(frozen=True) + class SolidType: + VTK_SOLID_TETRAHEDRON: int = 0 + VTK_SOLID_CUBE: int = 1 + VTK_SOLID_OCTAHEDRON: int = 2 + VTK_SOLID_ICOSAHEDRON: int = 3 + VTK_SOLID_DODECAHEDRON: int = 4 + ``` ### Property diff --git a/src/PythonicAPI/Utilities/ArrayWriter.py b/src/PythonicAPI/Utilities/ArrayWriter.py new file mode 100644 index 0000000000000000000000000000000000000000..e287a2c6f5fbdb5a1ad41c595991bbae8836613f --- /dev/null +++ b/src/PythonicAPI/Utilities/ArrayWriter.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonCore import vtkDenseArray +from vtkmodules.vtkCommonDataModel import vtkArrayData +from vtkmodules.vtkIOCore import vtkArrayWriter + + +def main(): + # This is a templated class, note the use of square brackets for the template arguments. + array = vtkDenseArray['float']() + array.Resize(1, 3) + array.SetValue(0, 0, 1.0) + array.SetValue(0, 1, 2.0) + array.SetValue(0, 2, 3.0) + + print(f'The extents are are: ({array.extents[0].GetEnd()}, {array.extents[1].GetEnd()})') + # Set the values. + for i in range(0, array.extents[0].GetEnd()): + for j in range(0, array.extents[1].GetEnd()): + array.SetValue(i, j, i + j) + + # Method 1 + array_data = vtkArrayData() + array_data.AddArray(array) + + writer1 = vtkArrayWriter(file_name='Test1.txt', input_data=array_data) + writer1.Write() + + # Method 2 + file_name = 'Test2.txt' + writer2 = vtkArrayWriter() + writer2.Write(array, file_name) + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Widgets/BorderWidget.md b/src/PythonicAPI/Widgets/BorderWidget.md new file mode 100644 index 0000000000000000000000000000000000000000..9410d4da05b94453ff0668525ac227239dfaa5a8 --- /dev/null +++ b/src/PythonicAPI/Widgets/BorderWidget.md @@ -0,0 +1,3 @@ +### Description + +This example draws a border around a region selected with the mouse. Note that the default border color is white - so if you have a white background you will not see anything! diff --git a/src/PythonicAPI/Widgets/BorderWidget.py b/src/PythonicAPI/Widgets/BorderWidget.py new file mode 100644 index 0000000000000000000000000000000000000000..e14cd4eeb3f5a36133471e97f5b32ef9ed5c31c2 --- /dev/null +++ b/src/PythonicAPI/Widgets/BorderWidget.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import ( + vtkLookupTable +) +from vtkmodules.vtkFiltersSources import vtkPlatonicSolidSource +from vtkmodules.vtkInteractionWidgets import ( + vtkBorderRepresentation, + vtkBorderWidget + +) +from vtkmodules.vtkRenderingCore import ( + vtkActor, + vtkPolyDataMapper, + vtkRenderWindow, + vtkRenderWindowInteractor, + vtkRenderer +) + + +def main(): + colors = vtkNamedColors() + + lut = get_platonic_lut() + + # A renderer, render window and interactor. + ren = vtkRenderer(background=colors.GetColor3d('SteelBlue')) + ren_win = vtkRenderWindow(window_name='BorderWidget') + ren_win.AddRenderer(ren) + iren = vtkRenderWindowInteractor() + iren.render_window = ren_win + + platonic_solid = vtkPlatonicSolidSource(solid_type=PlatonicSolidSource.SolidType.VTK_SOLID_DODECAHEDRON) + mapper = vtkPolyDataMapper(lookup_table=lut, scalar_range=(0, 19)) + platonic_solid >> mapper + actor = vtkActor(mapper=mapper) + ren.AddActor(actor) + + # Create the widget and its representation + rep = vtkBorderRepresentation(proportional_resize=True, show_border=True) + # This has no effect, the border remains white. + rep.border_property.color = colors.GetColor3d('Yellow') + + widget = vtkBorderWidget(interactor=iren, representation=rep, selectable=False) + widget.AddObserver('EndInteractionEvent', BorderCallback(ren)) + + ren_win.Render() + ren.active_camera.Elevation(30.0) + ren.active_camera.Azimuth(180.0) + + iren.Initialize() + ren_win.Render() + widget.On() + iren.Start() + + +def get_platonic_lut(): + """ + Get a specialised lookup table for the platonic solids. + + Since each face of a vtkPlatonicSolidSource has a different + cell scalar, we create a lookup table with a different colour + for each face. + The colors have been carefully chosen so that adjacent cells + are colored distinctly. + + :return: The lookup table. + """ + lut = vtkLookupTable(number_of_table_values=20, table_range=(0.0, 19.0)) + # lut.SetNumberOfTableValues(20) + # lut.SetTableRange(0.0, 19.0) + lut.Build() + lut.SetTableValue(0, 0.1, 0.1, 0.1) + lut.SetTableValue(1, 0, 0, 1) + lut.SetTableValue(2, 0, 1, 0) + lut.SetTableValue(3, 0, 1, 1) + lut.SetTableValue(4, 1, 0, 0) + lut.SetTableValue(5, 1, 0, 1) + lut.SetTableValue(6, 1, 1, 0) + lut.SetTableValue(7, 0.9, 0.7, 0.9) + lut.SetTableValue(8, 0.5, 0.5, 0.5) + lut.SetTableValue(9, 0.0, 0.0, 0.7) + lut.SetTableValue(10, 0.5, 0.7, 0.5) + lut.SetTableValue(11, 0, 0.7, 0.7) + lut.SetTableValue(12, 0.7, 0, 0) + lut.SetTableValue(13, 0.7, 0, 0.7) + lut.SetTableValue(14, 0.7, 0.7, 0) + lut.SetTableValue(15, 0, 0, 0.4) + lut.SetTableValue(16, 0, 0.4, 0) + lut.SetTableValue(17, 0, 0.4, 0.4) + lut.SetTableValue(18, 0.4, 0, 0) + lut.SetTableValue(19, 0.4, 0, 0.4) + return lut + + +class BorderCallback(object): + def __init__(self, ren): + self.ren = ren + + def __call__(self, caller, ev): + # Just do this to demonstrate who called callback and the event that triggered it. + # print(caller.GetClassName(), 'Event Id:', ev) + rep = caller.representation + # Viewport coordinates. + lower_left = rep.position + upper_right = rep.position2 + print('Viewport coordinates:') + print(f'Lower left: ({lower_left[0]:5.2f}, {lower_left[1]:5.2f}),') + print(f'Upper right: ({lower_left[0] + upper_right[0]:5.2f}, {lower_left[1] + upper_right[1]:5.2f}),') + # World coordinates. + # lower_left_coordinate = rep.position_coordinate + # lower_left = lower_left_coordinate.GetComputedWorldValue(self.ren) + # upper_right_coordinate = rep.position2_coordinate + # upper_right = upper_right_coordinate.GetComputedWorldValue(self.ren) + # print('World coordinates:') + # print(f'Lower left: ({lower_left[0]:5.2f}, {lower_left[1]:5.2f}),') + # print(f'Upper right: ({upper_right[0]:5.2f}, {upper_right[1]:5.2f}),') + + +@dataclass(frozen=True) +class PlatonicSolidSource: + @dataclass(frozen=True) + class SolidType: + VTK_SOLID_TETRAHEDRON: int = 0 + VTK_SOLID_CUBE: int = 1 + VTK_SOLID_OCTAHEDRON: int = 2 + VTK_SOLID_ICOSAHEDRON: int = 3 + VTK_SOLID_DODECAHEDRON: int = 4 + + +if __name__ == '__main__': + main() diff --git a/src/Testing/Baseline/PythonicAPI/Widgets/TestBorderWidget.png b/src/Testing/Baseline/PythonicAPI/Widgets/TestBorderWidget.png new file mode 100644 index 0000000000000000000000000000000000000000..1785686db4c8b74d1e3c0e26b4e005c9e27eecd4 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Widgets/TestBorderWidget.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba65c407c19a373d3796b0ebe950bd71849306a098b57a4295708ab9bfe336dc +size 4369