Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
133 changes: 68 additions & 65 deletions pulse/interface/viewer_3d/actors/tube_actor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

import numpy as np
from vtkmodules.util.numpy_support import numpy_to_vtk
from vtkmodules.vtkCommonCore import vtkDoubleArray, vtkIntArray, vtkPoints, vtkUnsignedCharArray
from vtkmodules.vtkCommonDataModel import vtkPlane, vtkPolyData
from vtkmodules.vtkFiltersCore import vtkPolyDataNormals
Expand All @@ -9,9 +10,7 @@
from pulse import app
from pulse.interface.viewer_3d.coloring.color_table import ColorTable
from pulse.model.cross_section import CrossSection
from pulse.model.node import Node
from pulse.model.elements.element_attributes import ElementAttributes

from pulse.utils import cross_section_sources
from pulse.utils.interface_utils import ColorMode

Expand All @@ -33,12 +32,8 @@ class TubeActor(vtkActor):

def __init__(self, **kwargs) -> None:
super().__init__()

self.user_preferences = app().main_window.config.user_preferences
self.elements_attributes = app().project.model.preprocessor.elements_attributes
self.deformed_coordinates = app().project.model.preprocessor.deformed_coordinates

self.hidden_elements = kwargs.get("hidden_elements", set())
self.build()

@property
Expand All @@ -49,62 +44,45 @@ def model(self):
def preprocessor(self):
return app().project.model.preprocessor

def get_element_attributes(self, element_id: int):
return self.preprocessor.elements_attributes.get(element_id)

def build(self):

all_elements = np.array(list(self.elements_attributes.keys()), dtype=int)
visible_elements = all_elements[~np.isin(all_elements, self.hidden_elements)]

self._key_index = {j: i for i, j in enumerate(visible_elements)}

# visible_elements2 = {i: e for i, e in self.elements_attributes.items() if (i not in self.hidden_elements)}
# self._key_index2 = {j: i for i, j in enumerate(visible_elements2.keys())}

# aux_1 = np.array([list(self._key_index.values()), list(self._key_index2.values())]).T
# aux_2 = np.array([list(self._key_index.keys()), list(self._key_index2.keys())]).T

# print(np.max(aux_1[:, 0] - aux_1[:, 1]))
# print(np.max(aux_2[:, 0] - aux_2[:, 1]))
self._key_index = {j: i for i, j in enumerate(all_elements)}

data = vtkPolyData()
mapper = vtkGlyph3DMapper()

points = vtkPoints()
self.element_start_points = vtkPoints()
self.element_start_points.SetNumberOfPoints(len(all_elements))

self.element_rotations = vtkDoubleArray()
self.element_rotations.SetNumberOfComponents(3)
self.element_rotations.SetName("rotations")

sources = vtkIntArray()
sources.SetName("sources")

rotations = vtkDoubleArray()
rotations.SetNumberOfComponents(3)
rotations.SetName("rotations")

colors = vtkUnsignedCharArray()
colors.SetNumberOfComponents(3)
colors.SetNumberOfTuples(len(visible_elements))

colors.SetNumberOfTuples(len(all_elements))
colors.Fill(255)
colors.SetName("colors")

section_index = dict()
for element_id in visible_elements:
element_attributes = self.preprocessor.elements_attributes.get(element_id)
hashes = [self._hash_element_section(el) for el in self.elements_attributes.values()]
unique_hashes, first_occurrences, remapped_indexes = np.unique(hashes, return_index=True, return_inverse=True)
new_ids = np.arange(len(unique_hashes))

points.InsertNextPoint(self.get_element_coordinates(element_attributes.first_node))
rotations.InsertNextTuple(self.get_element_rotations(element_id))

key = self._hash_element_section(element_attributes)
if key not in section_index:
section_index[key] = len(section_index)
source = self.create_element_data(element_attributes)
source = self._fixed_section(source)
mapper.SetSourceData(section_index[key], source)
sources.DeepCopy(numpy_to_vtk(remapped_indexes))
sources.SetName("sources")

sources.InsertNextTuple1(section_index[key])
for new_id, unique_hash, element_index in zip(new_ids, unique_hashes, first_occurrences):
element_attributes = self.elements_attributes.get(element_index)
source = self.create_element_data(element_attributes)
source = self._fixed_section(source)
mapper.SetSourceData(new_id, source)

data.SetPoints(points)
data.SetPoints(self.element_start_points)
data.GetPointData().AddArray(sources)
data.GetPointData().AddArray(rotations)
data.GetPointData().AddArray(self.element_rotations)
data.GetPointData().SetScalars(colors)

mapper.SetInputData(data)
Expand All @@ -116,25 +94,50 @@ def build(self):
mapper.SetScalarModeToUsePointData()
mapper.ScalarVisibilityOn()
mapper.Update()

self.SetMapper(mapper)

self.GetProperty().SetInterpolationToPhong()
self.GetProperty().SetDiffuse(0.8)
# self.GetProperty().SetSpecular(1.5)
# self.GetProperty().SetSpecularPower(80)
# self.GetProperty().SetSpecularColor(1, 1, 1)


self.update_element_coordinates_and_rotations()
self.clear_colors()

def get_element_coordinates(self, node: Node) -> tuple[float, float, float]:
return node.coordinates
def get_all_elements_coordinates(self) -> np.ndarray:
mesh = self.model.mesh
return mesh.nodal_coordinates[mesh.lines_connectivity[:, 4], 1:]

def get_element_rotations(self, element_id: int) -> tuple[float, float, float]:
return self.preprocessor.undeformed_section_rotations[element_id, :]
def get_all_elements_rotations(self):
return self.preprocessor.undeformed_section_rotations

def create_element_data(self, element_attributes: ElementAttributes):
def update_element_coordinates_and_rotations(self):
coordinates = self.get_all_elements_coordinates()
rotations = self.get_all_elements_rotations()

mapper = self.GetMapper()
if mapper is None:
return

data: vtkPolyData | None = mapper.GetInput()
if data is None:
return

points: vtkPoints | None = data.GetPoints()
if points is None:
return

point_data = data.GetPointData()
if point_data is None:
return

rotations_array = point_data.GetArray("rotations")
if rotations_array is None:
return

points.SetData(numpy_to_vtk(coordinates))
rotations_array.DeepCopy(numpy_to_vtk(rotations))
rotations_array.SetName("rotations")

def create_element_data(self, element_attributes: ElementAttributes):
cross_section = element_attributes.cross_section
length = element_attributes.length
section_parameters_render = element_attributes.section_parameters_render
Expand All @@ -146,34 +149,34 @@ def create_element_data(self, element_attributes: ElementAttributes):

if cross_section.section_type_label in ["pipe", "bend", "arc_bend", "reducer"]:
if section_parameters_render is None:
d_out, t, offset_y, offset_z, *_ = cross_section.section_parameters
d_out, t, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
else:
d_out, t, offset_y, offset_z, *_ = section_parameters_render
d_out, t, offset_y, offset_z, *_ = np.round(section_parameters_render, 5)

return cross_section_sources.pipe_data(length, d_out, t, offset_y, offset_z, sides=tube_sides)

elif cross_section.section_type_label == "rectangular_beam":
b, h, b_in, h_in, offset_y, offset_z, *_ = cross_section.section_parameters
b, h, b_in, h_in, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
return cross_section_sources.rectangular_beam_data(length, b, h, b_in, h_in, offset_y=offset_y, offset_z=offset_z)

elif cross_section.section_type_label == "circular_beam":
d_out, t, offset_y, offset_z, *_ = cross_section.section_parameters
d_out, t, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
return cross_section_sources.circular_beam_data(length, d_out, t, offset_y=offset_y, offset_z=offset_z)

elif cross_section.section_type_label == "c_beam":
h, w1, t1, w2, t2, tw, offset_y, offset_z, *_ = cross_section.section_parameters
h, w1, t1, w2, t2, tw, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
return cross_section_sources.c_beam_data(length, h, w1, w2, t1, t2, tw, offset_y=offset_y, offset_z=offset_z)

elif cross_section.section_type_label == "i_beam":
h, w1, t1, w2, t2, tw, offset_y, offset_z, *_ = cross_section.section_parameters
h, w1, t1, w2, t2, tw, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
return cross_section_sources.i_beam_data(length, h, w1, w2, t1, t2, tw, offset_y=offset_y, offset_z=offset_z)

elif cross_section.section_type_label == "t_beam":
h, w1, t1, tw, offset_y, offset_z, *_ = cross_section.section_parameters
h, w1, t1, tw, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
return cross_section_sources.t_beam_data(length, h, w1, t1, tw, offset_y=offset_y, offset_z=offset_z)

elif cross_section.section_type_label == "expansion_joint":
d_eff, offset_y, offset_z, plot_key = section_parameters_render
d_eff, offset_y, offset_z, plot_key = np.round(section_parameters_render, 5)

if plot_key == "major":
d_out = d_eff * 1.25
Expand All @@ -192,7 +195,7 @@ def create_element_data(self, element_attributes: ElementAttributes):
return cross_section_sources.pipe_data(length, d_out, t, offset_y=offset_y, offset_z=offset_z, sides=tube_sides)

elif cross_section.section_type_label == "valve":
d_out, t, offset_y, offset_z, *_ = section_parameters_render
d_out, t, offset_y, offset_z, *_ = np.round(section_parameters_render, 5)
return cross_section_sources.pipe_data(length, d_out, t, offset_y=offset_y, offset_z=offset_z, sides=tube_sides)

else:
Expand Down Expand Up @@ -332,7 +335,7 @@ def disable_cut(self):
self.GetMapper().RemoveAllClippingPlanes()

def _hash_element_section(self, element_attributes: ElementAttributes):

cross_section = element_attributes.cross_section
if cross_section is None:
return 0
Expand All @@ -357,4 +360,4 @@ def _fixed_section(self, source):
normals_filter.AddInputData(source)
normals_filter.Update()

return normals_filter.GetOutput()
return normals_filter.GetOutput()
38 changes: 27 additions & 11 deletions pulse/interface/viewer_3d/actors/tube_actor_results.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
import logging

import numpy as np
from vtkmodules.vtkCommonDataModel import vtkPolyData

from pulse.interface.viewer_3d.actors import TubeActor
from pulse.model.cross_section import CrossSection
from pulse.model.node import Node
from pulse.model.elements.element_attributes import ElementAttributes
from pulse.utils import cross_section_sources

logger = logging.getLogger(__name__)


class TubeActorResults(TubeActor):
def __init__(self, acoustic_plot: bool = False, show_deformed: bool = False, **kwargs) -> None:
self.acoustic_plot = acoustic_plot
self.show_deformed = show_deformed
super().__init__(**kwargs)

def get_element_coordinates(self, node: Node) -> tuple[float, float, float]:
return self.deformed_coordinates[node.index, 1:] if self.show_deformed else node.coordinates
def get_all_elements_coordinates(self) -> np.ndarray:
if not self.show_deformed:
return super().get_all_elements_coordinates()

deformed_coordinates = self.model.preprocessor.deformed_coordinates
if deformed_coordinates is None:
logger.warn("Invalid deformed coordinates.")
return super().get_all_elements_coordinates()

mesh = self.model.mesh
return deformed_coordinates[mesh.lines_connectivity[:, 4], 1:]

def get_all_elements_rotations(self):
if not self.show_deformed:
return super().get_all_elements_rotations()

def get_element_rotations(self, element_id: int) -> tuple[float, float, float]:
if self.show_deformed:
return self.preprocessor.deformed_section_rotations[element_id, :]
if self.preprocessor.deformed_section_rotations is None:
logger.warn("Invalid deformed section rotations.")
return super().get_all_elements_rotations()

return self.preprocessor.undeformed_section_rotations[element_id, :]
return self.preprocessor.deformed_section_rotations

def create_element_data(self, element_attributes: ElementAttributes):

Expand All @@ -40,15 +57,14 @@ def create_element_data(self, element_attributes: ElementAttributes):
# In acoustic plots we need to show the fluids, not the pipe
if self.acoustic_plot:
length = element_attributes.length
section_parameters_render = element_attributes.section_parameters_render

if pipe_section or valve:
d_out, t, offset_y, offset_z, *_ = cross_section.section_parameters
d_out, t, offset_y, offset_z, *_ = np.round(cross_section.section_parameters, 5)
d_inner = d_out - 2 * t
return cross_section_sources.closed_pipe_data(length, d_inner, offset_y=offset_y, offset_z=offset_z, sides=tube_sides)

elif expansion_joint:
d_eff, offset_y, offset_z, *_ = section_parameters_render
d_eff, offset_y, offset_z, *_ = np.round(element_attributes.section_parameters_render, 5)
return cross_section_sources.closed_pipe_data(length, d_eff, offset_y=offset_y, offset_z=offset_z, sides=tube_sides)

return super().create_element_data(element_attributes)
return super().create_element_data(element_attributes)
Loading
Loading