Giter Club home page Giter Club logo

Comments (4)

darikg avatar darikg commented on June 3, 2024

See data arrays documentation.

from pyvista import Plane
m0 = Plane(i_resolution=1, j_resolution=2)
m1 = Plane(i_resolution=1, j_resolution=3)
m0.cell_data['tag'] = 0
m1.cell_data['tag'] = 1
m = m0.merge(m1)
m.cell_data['tag']

pyvista_ndarray([1., 1., 1., 0., 0.])

It also works with strings but you need to manually expand the string to the number of cells (faces):

m0.cell_data['tag'] = ['front' for _ in range(m0.n_cells)]
m1.cell_data['tag'] = ['back' for _ in range(m1.n_cells)]
print(m0.merge(m1).cell_data['tag'])

['back' 'back' 'back' 'front' 'front']

from pyvista.

fmamitrotta avatar fmamitrotta commented on June 3, 2024

Cool, thanks for pointing me in this direction, I can indeed tag the faces of my mesh in the way I want now!

I'd like to do something similar with point_data, but a bit more involved. I would like to tag in an analogous way the points of my faces, however this time one point can belong to one or more parts of the mesh. For example, at the intersections of my box beam, a point can belong at the same time to the "top skin", "rib" and "stiffener". As a consequence, the tag should now be a list of strings rather than a string.

What can I do to obtain this behavior?

At the moment, the function that I use to mesh each segment of my box beam ends in the following way:

def mesh_between_profiles(start_profile_xyz_array, end_profile_xyz_array, no_nodes, tag):
    # Initial part of the function to find mesh_xyz_array and faces
    # ...
    # Final part of the function
    mesh_polydata = pv.PolyData()
    mesh_polydata.points = mesh_xyz_array
    mesh_polydata.point_data['tag'] = [tag]*mesh_polydata.n_points
    mesh_polydata.faces = faces
    mesh_polydata.cell_data['tag'] = [tag]*mesh_polydata.n_cells
    return mesh_polydata

However, when I merge all the different PolyData objects together and clean the final mesh, the point_data['tag'] attribute of the duplicated nodes are overwritten. Is there a way to avoid this?

Another option that I've thought is to populate the point_data['tag'] only at the end, when the final cleaned mesh is done, combining the tags of the parent faces. However I guess that means iterating over every cell, so it wouldn't be very quick for a very large mesh.

from pyvista.

darikg avatar darikg commented on June 3, 2024

Yeah, point data is definitely trickier for that exact reason, the clean filter throws out point data when merging.

Here's a kludge you may or may not find useful. It uses two tricks: 1) data arrays can have multiple columns and 2) the 'cell_data_to_point_data` filter calculates for each point the average of the data of the faces containing that point.

from pyvista import Plane
from numpy import zeros, array

m0 = Plane(i_resolution=2, j_resolution=2, center=(0, 0, 0))
m1 = Plane(i_resolution=2, j_resolution=2, center=(1, 0, 0))
m2 = Plane(i_resolution=2, j_resolution=2, center=(2, 0, 0))

# Map tag names to numbers for convenience and speed
tag_ids = dict(front=1, side=2, back=3)
m0.cell_data['tag'] = tag_ids['front']
m1.cell_data['tag'] = tag_ids['side']
m2.cell_data['tag'] = tag_ids['back']
merged = m0.merge([m1, m2]).clean()

# Convert from categorical tags to indicator variables
# Make a (n_cells, n_tags) array which is 1 if that face has that tag, otherwise 0
tag_data = zeros((merged.n_cells, len(tag_ids)))
for i, tag_id in enumerate(tag_ids.values()):
    tag_data[:, i] = merged.cell_data['tag'] == tag_id

merged.cell_data['tag_data'] = tag_data

# Use the 'cell_data_to_point_data' filter
# Notably this filter seems to ignore string-valued arrays
ctp = merged.cell_data_to_point_data()
points_tag_data = ctp.point_data['tag_data']  # (n_points, n_tags)

# Convert back to names
# Because of the averaging, the values in `points_tag_data` will be 0.0 if that point
# never belonged to a face with that tag, 1.0 if all of the faces had that tag,
# and some number inbetween for mixed face tags
names = array(list(tag_ids.keys()))
merged.point_data['tag_names'] = [
    ', '.join(names[avg_indicators > 0])
    for avg_indicators in points_tag_data
]

from pyvista.

fmamitrotta avatar fmamitrotta commented on June 3, 2024

Thanks for your suggestion @darikg, it is a clever workaround.

However, I'm not a big fan of mapping tag names to numbers because I believe the tag structure loses a bit of clarity, even if I do understand the advantages of using such approach.

I was inspired by your answer and played a bit around with my code, and I came up with the following solution to populate the point_data attribute after the merged clean mesh is obtained.

# Initial part of the script generating the PolyData objects stored as in the list meshes
# ...
# Merge all the mesh segments together
merged_box_beam_mesh = meshes[0].merge(meshes[1:])
# Clean the obtained mesh by merging points closer than a specified tolerance
cleaned_box_beam_mesh = merged_box_beam_mesh.clean(tolerance=element_length / 100)

# Tag the points based on the faces they belong to
# Step 1: gather the indices of points that form each face
cells = cleaned_box_beam_mesh.faces.reshape(-1, 5)[:, 1:]  # assume quad cells
point_indices = cells.flatten()  # flatten the cells array to get a list of point indices, repeated per cell occurrence
cell_tags_repeated = np.repeat(cleaned_box_beam_mesh.cell_data['tag'], 4)  # array of the same shape as point_indices, where each cell tag is repeated for each of its points
# Step 2: Map cell tags to point tags using an indirect sorting approach
sort_order = np.argsort(point_indices)  # get the sort order to rearrange the point indices
sorted_point_indices = point_indices[sort_order]  # sort the point indices
sorted_tags = cell_tags_repeated[sort_order]  # sort the cell tags in the same order
_, boundaries_indices = np.unique(sorted_point_indices, return_index=True)  # find the boundaries between different points in the sorted point indices
# Step 3: Split the sorted tags array at these boundaries to get lists of tags for each point
tags_split = np.split(sorted_tags, boundaries_indices[1:])  # split the sorted tags array at the boundaries to get lists of tags for each point
point_tags_list = np.array([', '.join(tags) for tags in tags_split])  # convert each list of tags into a comma-separated string
cleaned_box_beam_mesh.point_data['tags'] = point_tags_list  # apply the tags to the point_data of the mesh

Then I can easily retrieve the indices of the points of a part of my mesh with the following code.

nodes_xyz_array = cleaned_box_beam_mesh.points  # get xyz coordinates of all nodes
top_skin_points_indices = np.flatnonzero(np.char.find(cleaned_box_beam_mesh.point_data['tags'], "top skin")!=-1)  # find indices of top skin points by looking for "top skin" tag

For the moment, this achieves what I want.

Nevertheless, would it still be worth considering the implementation of a more automatic way to handle literal tags of faces and points?

from pyvista.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.