| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "FreeCAD FEM postprocessing data exxtractor base objects" |
| | __author__ = "Stefan Tröger" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| | |
| |
|
| | from vtkmodules.vtkCommonCore import vtkIntArray |
| | from vtkmodules.vtkCommonCore import vtkDoubleArray |
| | from vtkmodules.vtkCommonDataModel import vtkTable |
| |
|
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| |
|
| | from . import base_fempythonobject |
| |
|
| | _PropHelper = base_fempythonobject._PropHelper |
| |
|
| | |
| | |
| |
|
| |
|
| | def is_extractor_object(obj): |
| | if not hasattr(obj, "Proxy"): |
| | return False |
| |
|
| | return hasattr(obj.Proxy, "ExtractionType") |
| |
|
| |
|
| | def get_extraction_type(obj): |
| | |
| | |
| | return obj.Proxy.ExtractionType |
| |
|
| |
|
| | def get_extraction_dimension(obj): |
| | |
| | |
| | return obj.Proxy.ExtractionDimension |
| |
|
| |
|
| | |
| | |
| | |
| | class Extractor(base_fempythonobject.BaseFemPythonObject): |
| |
|
| | def __init__(self, obj): |
| | super().__init__(obj) |
| | self._setup_properties(obj) |
| |
|
| | def _setup_properties(self, obj): |
| | pl = obj.PropertiesList |
| | for prop in self._get_properties(): |
| | if not prop.name in pl: |
| | prop.add_to_object(obj) |
| |
|
| | def _get_properties(self): |
| |
|
| | prop = [ |
| | _PropHelper( |
| | type="Fem::PropertyPostDataObject", |
| | name="Table", |
| | group="Base", |
| | doc=QT_TRANSLATE_NOOP("FEM", "The data table that stores the extracted data"), |
| | value=vtkTable(), |
| | ), |
| | _PropHelper( |
| | type="App::PropertyLink", |
| | name="Source", |
| | group="Base", |
| | doc=QT_TRANSLATE_NOOP("FEM", "The data source from which the data is extracted"), |
| | value=None, |
| | ), |
| | ] |
| | return prop |
| |
|
| | def onDocumentRestored(self, obj): |
| | self._setup_properties(obj) |
| |
|
| | def onChanged(self, obj, prop): |
| |
|
| | if prop == "Source": |
| | |
| | if obj.Source and not obj.Source.isDerivedFrom("Fem::FemPostObject"): |
| | FreeCAD.Console.PrintWarning("Invalid object: Line source must be FemPostObject") |
| | obj.Source = None |
| |
|
| | def get_vtk_table(self, obj): |
| | if not obj.DataTable: |
| | obj.DataTable = vtkTable() |
| |
|
| | return obj.DataTable |
| |
|
| | def component_options(self, num): |
| |
|
| | match num: |
| | case 2: |
| | return ["X", "Y"] |
| | case 3: |
| | return ["X", "Y", "Z"] |
| | case 6: |
| | return ["XX", "YY", "ZZ", "XY", "XZ", "YZ"] |
| | case _: |
| | return ["Not a vector"] |
| |
|
| | def get_representive_fieldname(self, obj): |
| | |
| | return "" |
| |
|
| |
|
| | class Extractor1D(Extractor): |
| |
|
| | ExtractionDimension = "1D" |
| |
|
| | def __init__(self, obj): |
| | super().__init__(obj) |
| |
|
| | def _get_properties(self): |
| | prop = [ |
| | _PropHelper( |
| | type="App::PropertyEnumeration", |
| | name="XField", |
| | group="X Data", |
| | doc=QT_TRANSLATE_NOOP("FEM", "The field to use as X data"), |
| | value=[], |
| | ), |
| | _PropHelper( |
| | type="App::PropertyEnumeration", |
| | name="XComponent", |
| | group="X Data", |
| | doc=QT_TRANSLATE_NOOP( |
| | "FEM", "Which part of the X field vector to use for the X axis" |
| | ), |
| | value=[], |
| | ), |
| | ] |
| |
|
| | return super()._get_properties() + prop |
| |
|
| | def onChanged(self, obj, prop): |
| |
|
| | super().onChanged(obj, prop) |
| |
|
| | if prop == "XField" and obj.Source and obj.Source.getDataSet(): |
| | point_data = obj.Source.getDataSet().GetPointData() |
| | self._setup_x_component_property(obj, point_data) |
| |
|
| | if prop == "Source": |
| | if obj.Source: |
| | dset = obj.Source.getDataSet() |
| | if dset: |
| | self._setup_x_properties(obj, dset) |
| | else: |
| | self._clear_x_properties(obj) |
| | else: |
| | self._clear_x_properties(obj) |
| |
|
| | def _setup_x_component_property(self, obj, point_data): |
| |
|
| | if obj.XField == "Index": |
| | obj.XComponent = self.component_options(1) |
| | elif obj.XField == "Position": |
| | obj.XComponent = self.component_options(3) |
| | else: |
| | array = point_data.GetAbstractArray(obj.XField) |
| | obj.XComponent = self.component_options(array.GetNumberOfComponents()) |
| |
|
| | def _clear_x_properties(self, obj): |
| | if hasattr(obj, "XComponent"): |
| | obj.XComponent = [] |
| | if hasattr(obj, "XField"): |
| | obj.XField = [] |
| |
|
| | def _setup_x_properties(self, obj, dataset): |
| | |
| | fields = ["Index", "Position"] |
| | point_data = dataset.GetPointData() |
| |
|
| | for i in range(point_data.GetNumberOfArrays()): |
| | fields.append(point_data.GetArrayName(i)) |
| |
|
| | current_field = obj.XField |
| | obj.XField = fields |
| | if current_field in fields: |
| | obj.XField = current_field |
| |
|
| | self._setup_x_component_property(obj, point_data) |
| |
|
| | def _x_array_component_to_table(self, obj, array, table): |
| | |
| | |
| |
|
| | if array.GetNumberOfComponents() == 1: |
| | table.AddColumn(array) |
| | else: |
| | component_array = vtkDoubleArray() |
| | component_array.SetNumberOfComponents(1) |
| | component_array.SetNumberOfTuples(array.GetNumberOfTuples()) |
| | c_idx = obj.getEnumerationsOfProperty("XComponent").index(obj.XComponent) |
| | component_array.CopyComponent(0, array, c_idx) |
| | component_array.SetName(array.GetName()) |
| | table.AddColumn(component_array) |
| |
|
| | def _x_array_from_dataset(self, obj, dataset, copy=True): |
| | |
| | |
| |
|
| | match obj.XField: |
| | case "Index": |
| | |
| | num = dataset.GetPoints().GetNumberOfPoints() |
| | array = vtkIntArray() |
| | array.SetNumberOfTuples(num) |
| | array.SetNumberOfComponents(1) |
| | for i in range(num): |
| | array.SetValue(i, i) |
| |
|
| | case "Position": |
| |
|
| | orig_array = dataset.GetPoints().GetData() |
| | if copy: |
| | array = vtkDoubleArray() |
| | array.DeepCopy(orig_array) |
| | else: |
| | array = orig_array |
| |
|
| | case _: |
| | point_data = dataset.GetPointData() |
| | orig_array = point_data.GetAbstractArray(obj.XField) |
| | if copy: |
| | array = vtkDoubleArray() |
| | array.DeepCopy(orig_array) |
| | else: |
| | array = orig_array |
| |
|
| | return array |
| |
|
| | def get_representive_fieldname(self, obj): |
| | |
| | label = obj.XField |
| | if not label: |
| | return "" |
| |
|
| | if len(obj.getEnumerationsOfProperty("XComponent")) > 1: |
| | label += f" ({obj.XComponent})" |
| |
|
| | return label |
| |
|
| |
|
| | class Extractor2D(Extractor1D): |
| |
|
| | ExtractionDimension = "2D" |
| |
|
| | def __init__(self, obj): |
| | super().__init__(obj) |
| |
|
| | def _get_properties(self): |
| | prop = [ |
| | _PropHelper( |
| | type="App::PropertyEnumeration", |
| | name="YField", |
| | group="Y Data", |
| | doc=QT_TRANSLATE_NOOP("FEM", "The field to use as Y data"), |
| | value=[], |
| | ), |
| | _PropHelper( |
| | type="App::PropertyEnumeration", |
| | name="YComponent", |
| | group="Y Data", |
| | doc=QT_TRANSLATE_NOOP( |
| | "FEM", "Which part of the Y field vector to use for the Y axis" |
| | ), |
| | value=[], |
| | ), |
| | ] |
| |
|
| | return super()._get_properties() + prop |
| |
|
| | def onChanged(self, obj, prop): |
| |
|
| | super().onChanged(obj, prop) |
| |
|
| | if prop == "YField" and obj.Source and obj.Source.getDataSet(): |
| | point_data = obj.Source.getDataSet().GetPointData() |
| | self._setup_y_component_property(obj, point_data) |
| |
|
| | if prop == "Source": |
| | if obj.Source: |
| | dset = obj.Source.getDataSet() |
| | if dset: |
| | self._setup_y_properties(obj, dset) |
| | else: |
| | self._clear_y_properties(obj) |
| | else: |
| | self._clear_y_properties(obj) |
| |
|
| | def _setup_y_component_property(self, obj, point_data): |
| |
|
| | if obj.YField == "Position": |
| | obj.YComponent = self.component_options(3) |
| | else: |
| | array = point_data.GetAbstractArray(obj.YField) |
| | obj.YComponent = self.component_options(array.GetNumberOfComponents()) |
| |
|
| | def _clear_y_properties(self, obj): |
| | if hasattr(obj, "YComponent"): |
| | obj.YComponent = [] |
| | if hasattr(obj, "YField"): |
| | obj.YField = [] |
| |
|
| | def _setup_y_properties(self, obj, dataset): |
| | |
| | fields = ["Position"] |
| | point_data = dataset.GetPointData() |
| |
|
| | for i in range(point_data.GetNumberOfArrays()): |
| | fields.append(point_data.GetArrayName(i)) |
| |
|
| | current_field = obj.YField |
| | obj.YField = fields |
| | if current_field in fields: |
| | obj.YField = current_field |
| |
|
| | self._setup_y_component_property(obj, point_data) |
| |
|
| | def _y_array_component_to_table(self, obj, array, table): |
| | |
| |
|
| | if array.GetNumberOfComponents() == 1: |
| | table.AddColumn(array) |
| | else: |
| | component_array = vtkDoubleArray() |
| | component_array.SetNumberOfComponents(1) |
| | component_array.SetNumberOfTuples(array.GetNumberOfTuples()) |
| | c_idx = obj.getEnumerationsOfProperty("YComponent").index(obj.YComponent) |
| | component_array.CopyComponent(0, array, c_idx) |
| | component_array.SetName(array.GetName()) |
| | table.AddColumn(component_array) |
| |
|
| | def _y_array_from_dataset(self, obj, dataset, copy=True): |
| | |
| | |
| |
|
| | match obj.YField: |
| | case "Position": |
| |
|
| | orig_array = dataset.GetPoints().GetData() |
| | if copy: |
| | array = vtkDoubleArray() |
| | array.DeepCopy(orig_array) |
| | else: |
| | array = orig_array |
| |
|
| | case _: |
| | point_data = dataset.GetPointData() |
| | orig_array = point_data.GetAbstractArray(obj.YField) |
| |
|
| | if copy: |
| | array = vtkDoubleArray() |
| | array.DeepCopy(orig_array) |
| | else: |
| | array = orig_array |
| |
|
| | return array |
| |
|
| | def get_representive_fieldname(self, obj): |
| | |
| | label = obj.YField |
| | if not label: |
| | return "" |
| |
|
| | if len(obj.getEnumerationsOfProperty("YComponent")) > 1: |
| | label += f" ({obj.YComponent})" |
| |
|
| | return label |
| |
|