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
|