File size: 7,109 Bytes
985c397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# ***************************************************************************
# *   Copyright (c) 2025 Stefan Tröger <stefantroeger@gmx.net>              *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This program is free software; you can redistribute it and/or modify  *
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
# *   as published by the Free Software Foundation; either version 2 of     *
# *   the License, or (at your option) any later version.                   *
# *   for detail see the LICENCE text file.                                 *
# *                                                                         *
# *   This program is distributed in the hope that it will be useful,       *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this program; if not, write to the Free Software   *
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
# *   USA                                                                   *
# *                                                                         *
# ***************************************************************************

__title__ = "FreeCAD FEM postprocessing data visualization base object"
__author__ = "Stefan Tröger"
__url__ = "https://www.freecad.org"

## @package base_fempostextractors
#  \ingroup FEM
#  \brief base objects for data visualizations

from vtkmodules.vtkCommonDataModel import vtkTable
from vtkmodules.vtkCommonCore import vtkDoubleArray

from . import base_fempythonobject
from . import base_fempostextractors

# helper functions
# ################


def is_visualization_object(obj):
    if not obj:
        return False

    if not hasattr(obj, "Proxy"):
        return False

    return hasattr(obj.Proxy, "VisualizationType")


def get_visualization_type(obj):
    # returns the extractor type string, or throws exception if
    # not a extractor
    return obj.Proxy.VisualizationType


def is_visualization_extractor_type(obj, vistype):

    # must be extractor
    if not base_fempostextractors.is_extractor_object(obj):
        return False

    # must be visualization object
    if not is_visualization_object(obj):
        return False

    # must be correct type
    if get_visualization_type(obj) != vistype:
        return False

    return True


# Base class for all visualizations
# It collects all data from its extraction objects into a table.
# Note: Never use directly, always subclass! This class does not create a
#       Visualization variable, hence will not work correctly.
class PostVisualization(base_fempythonobject.BaseFemPythonObject):

    def __init__(self, obj):
        super().__init__(obj)
        self.Type = "Fem::FemPostVisualization"
        obj.addExtension("App::GroupExtensionPython")
        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):
        # override if subclass wants to add additional properties

        prop = [
            base_fempostextractors._PropHelper(
                type="Fem::PropertyPostDataObject",
                name="Table",
                group="Base",
                doc="The data table that stores the data for visualization",
                value=vtkTable(),
            ),
        ]
        return prop

    def onDocumentRestored(self, obj):
        # if a new property was added we handle it by setup
        # Override if subclass needs to handle changed property type

        self._setup_properties(obj)

    def onChanged(self, obj, prop):
        # Ensure only correct child object types are in the group

        if prop == "Group":
            # check if all objects are allowed

            children = obj.Group
            for child in obj.Group:
                if not is_visualization_extractor_type(child, self.VisualizationType):
                    FreeCAD.Console.PrintWarning(
                        f"{child.Label} is not a {self.VisualizationType} extraction object, cannot be added"
                    )
                    children.remove(child)

            if len(obj.Group) != len(children):
                obj.Group = children

    def execute(self, obj):
        # Collect all extractor child data into our table
        # Note: Each childs table can have different number of rows. We need
        # to pad the date for our table in this case

        rows = self.getLongestColumnLength(obj)
        table = vtkTable()
        for child in obj.Group:

            # If child has no Source, its table should be empty. However,
            # it would theoretical be possible that child source was set
            # to none without recompute, and the visualization was manually
            # recomputed afterwards
            if not child.Source and (child.Table.GetNumberOfColumns() > 0):
                FreeCAD.Console.PrintWarning(
                    f"{child.Label} has data, but no Source object. Will be ignored"
                )
                continue

            c_table = child.Table
            for i in range(c_table.GetNumberOfColumns()):
                c_array = c_table.GetColumn(i)
                array = vtkDoubleArray()

                if c_array.GetNumberOfTuples() == rows:
                    # simple deep copy is enough
                    array.DeepCopy(c_array)

                else:
                    array.SetNumberOfComponents(c_array.GetNumberOfComponents())
                    array.SetNumberOfTuples(rows)
                    array.Fill(0)  # so that all non-used entries are set to 0
                    for j in range(c_array.GetNumberOfTuples()):
                        array.SetTuple(j, c_array.GetTuple(j))

                array.SetName(f"{child.Source.Name}: {c_array.GetName()}")
                table.AddColumn(array)

        obj.Table = table
        return False

    def getLongestColumnLength(self, obj):
        # iterate all extractor children and get the column lengths

        length = 0
        for child in obj.Group:
            if base_fempostextractors.is_extractor_object(child):
                table = child.Table
                if table.GetNumberOfColumns() > 0:
                    # we assume all columns of an extractor have same length
                    num = table.GetColumn(0).GetNumberOfTuples()
                    if num > length:
                        length = num

        return length