face fields support
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
Helper function for exporting mesh to VTK PolyData XML format (.vtp) with vertex scalar attributes.
|
||||
Helper function for exporting mesh to VTK PolyData XML format (.vtp) with scalar attributes.
|
||||
|
||||
VTP format preserves vertex attributes (scalar fields) which can be visualized
|
||||
in VTK.js with color mapping.
|
||||
VTP format preserves vertex attributes (PointData) and face attributes (CellData)
|
||||
which can be visualized in VTK.js with color mapping.
|
||||
"""
|
||||
|
||||
import trimesh as trimesh_module
|
||||
@@ -11,13 +11,13 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
def export_mesh_with_scalars_vtp(trimesh: trimesh_module.Trimesh, filepath: str):
|
||||
"""
|
||||
Export trimesh to VTK PolyData XML format (.vtp) with vertex scalar attributes.
|
||||
Export trimesh to VTK PolyData XML format (.vtp) with scalar attributes.
|
||||
|
||||
VTP format preserves vertex attributes (scalar fields) which can be visualized
|
||||
in VTK.js with color mapping.
|
||||
VTP format preserves vertex attributes (PointData) and face attributes (CellData)
|
||||
which can be visualized in VTK.js with color mapping.
|
||||
|
||||
Args:
|
||||
trimesh: Trimesh object with optional vertex_attributes
|
||||
trimesh: Trimesh object with optional vertex_attributes and face_attributes
|
||||
filepath: Output .vtp file path
|
||||
"""
|
||||
print(f"[_export_mesh_with_scalars_vtp] Exporting to VTP: {filepath}")
|
||||
@@ -54,6 +54,19 @@ def export_mesh_with_scalars_vtp(trimesh: trimesh_module.Trimesh, filepath: str)
|
||||
format='ascii')
|
||||
scalar_array.text = ' '.join(map(str, attr_values.flatten()))
|
||||
|
||||
# CellData section (face attributes)
|
||||
cell_data = ET.SubElement(piece, 'CellData')
|
||||
|
||||
# Add face attributes as scalar arrays
|
||||
if hasattr(trimesh, 'face_attributes') and trimesh.face_attributes:
|
||||
for attr_name, attr_values in trimesh.face_attributes.items():
|
||||
print(f"[_export_mesh_with_scalars_vtp] Adding face field: {attr_name}")
|
||||
scalar_array = ET.SubElement(cell_data, 'DataArray',
|
||||
type='Float32',
|
||||
Name=attr_name,
|
||||
format='ascii')
|
||||
scalar_array.text = ' '.join(map(str, attr_values.flatten()))
|
||||
|
||||
# Polys section (faces/triangles)
|
||||
polys = ET.SubElement(piece, 'Polys')
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Preview mesh with VTK.js viewer optimized for scalar field visualization.
|
||||
|
||||
Specifically designed for visualizing vertex scalar fields (like boundary edges,
|
||||
curvature, distance fields, etc.) with color mapping. Always exports as VTP
|
||||
format to preserve scalar data.
|
||||
curvature, distance fields, etc.) and face scalar fields (like segmentation labels).
|
||||
Always exports as VTP format to preserve scalar data.
|
||||
"""
|
||||
|
||||
import trimesh as trimesh_module
|
||||
@@ -24,8 +24,8 @@ class PreviewMeshVTKPointCloudNode:
|
||||
Preview mesh with VTK.js viewer optimized for scalar field visualization.
|
||||
|
||||
Specifically designed for visualizing vertex scalar fields (like boundary edges,
|
||||
curvature, distance fields, etc.) with color mapping. Always exports as VTP
|
||||
format to preserve scalar data.
|
||||
curvature, distance fields, etc.) and face scalar fields (like segmentation labels).
|
||||
Always exports as VTP format to preserve scalar data.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@@ -53,15 +53,20 @@ class PreviewMeshVTKPointCloudNode:
|
||||
"""
|
||||
print(f"[PreviewMeshVTKPointCloud] Preparing preview: {len(trimesh.vertices)} vertices, {len(trimesh.faces)} faces")
|
||||
|
||||
# Check if mesh has vertex attributes (scalar fields)
|
||||
# Check if mesh has vertex or face attributes (scalar fields)
|
||||
has_vertex_attrs = hasattr(trimesh, 'vertex_attributes') and len(trimesh.vertex_attributes) > 0
|
||||
has_face_attrs = hasattr(trimesh, 'face_attributes') and len(trimesh.face_attributes) > 0
|
||||
|
||||
if hasattr(trimesh, 'vertex_attributes'):
|
||||
print(f"[PreviewMeshVTKPointCloud] trimesh.vertex_attributes: {trimesh.vertex_attributes.keys()}")
|
||||
print(f"[PreviewMeshVTKPointCloud] Number of vertex attributes: {len(trimesh.vertex_attributes)}")
|
||||
|
||||
if not has_vertex_attrs:
|
||||
print(f"[PreviewMeshVTKPointCloud] WARNING: No vertex attributes found. This node is for scalar field visualization.")
|
||||
if hasattr(trimesh, 'face_attributes'):
|
||||
print(f"[PreviewMeshVTKPointCloud] trimesh.face_attributes: {trimesh.face_attributes.keys()}")
|
||||
print(f"[PreviewMeshVTKPointCloud] Number of face attributes: {len(trimesh.face_attributes)}")
|
||||
|
||||
if not has_vertex_attrs and not has_face_attrs:
|
||||
print(f"[PreviewMeshVTKPointCloud] WARNING: No vertex or face attributes found. This node is for scalar field visualization.")
|
||||
|
||||
# Always use VTP format for this node (designed for scalar fields)
|
||||
filename = f"preview_vtk_pointcloud_{uuid.uuid4().hex[:8]}.vtp"
|
||||
@@ -72,11 +77,12 @@ class PreviewMeshVTKPointCloudNode:
|
||||
else:
|
||||
filepath = os.path.join(tempfile.gettempdir(), filename)
|
||||
|
||||
# Export mesh with vertex attributes as VTP
|
||||
# Export mesh with vertex and face attributes as VTP
|
||||
try:
|
||||
export_mesh_with_scalars_vtp(trimesh, filepath)
|
||||
num_fields = len(trimesh.vertex_attributes) if has_vertex_attrs else 0
|
||||
print(f"[PreviewMeshVTKPointCloud] Exported VTP with {num_fields} scalar fields to: {filepath}")
|
||||
num_vertex_fields = len(trimesh.vertex_attributes) if has_vertex_attrs else 0
|
||||
num_face_fields = len(trimesh.face_attributes) if has_face_attrs else 0
|
||||
print(f"[PreviewMeshVTKPointCloud] Exported VTP with {num_vertex_fields} vertex fields and {num_face_fields} face fields to: {filepath}")
|
||||
except Exception as e:
|
||||
print(f"[PreviewMeshVTKPointCloud] Export failed: {e}")
|
||||
import traceback
|
||||
|
||||
+70
-19
@@ -456,15 +456,36 @@
|
||||
for (let i = 0; i < numArrays; i++) {
|
||||
const array = pointData.getArray(i);
|
||||
const arrayName = array.getName();
|
||||
console.log(`[GeomPack VTK Fields Viewer] Found scalar field: ${arrayName}`);
|
||||
console.log(`[GeomPack VTK Fields Viewer] Found point field: ${arrayName}`);
|
||||
const option = document.createElement('option');
|
||||
option.value = arrayName;
|
||||
option.textContent = arrayName;
|
||||
option.value = `point:${arrayName}`;
|
||||
option.textContent = `${arrayName} (vertex)`;
|
||||
fieldSelector.appendChild(option);
|
||||
}
|
||||
fieldLabel.style.display = 'flex';
|
||||
console.log('[GeomPack VTK Fields Viewer] Scalar field selector populated with', numArrays, 'fields');
|
||||
} else {
|
||||
console.log('[GeomPack VTK Fields Viewer] Point data selector populated with', numArrays, 'fields');
|
||||
}
|
||||
|
||||
// Check for scalar fields in cell data (face attributes)
|
||||
const cellData = polydata.getCellData();
|
||||
const numCellArrays = cellData.getNumberOfArrays();
|
||||
console.log('[GeomPack VTK Fields Viewer] Cell data arrays:', numCellArrays);
|
||||
|
||||
if (numCellArrays > 0) {
|
||||
for (let i = 0; i < numCellArrays; i++) {
|
||||
const array = cellData.getArray(i);
|
||||
const arrayName = array.getName();
|
||||
console.log(`[GeomPack VTK Fields Viewer] Found face field: ${arrayName}`);
|
||||
const option = document.createElement('option');
|
||||
option.value = `cell:${arrayName}`;
|
||||
option.textContent = `${arrayName} (face)`;
|
||||
fieldSelector.appendChild(option);
|
||||
}
|
||||
fieldLabel.style.display = 'flex';
|
||||
console.log('[GeomPack VTK Fields Viewer] Cell data selector populated with', numCellArrays, 'fields');
|
||||
}
|
||||
|
||||
if (numArrays === 0 && numCellArrays === 0) {
|
||||
fieldLabel.style.display = 'none';
|
||||
console.log('[GeomPack VTK Fields Viewer] No scalar fields found');
|
||||
}
|
||||
@@ -472,7 +493,7 @@
|
||||
// Populate colormap selector
|
||||
const colormapSelector = document.getElementById('colormapSelector');
|
||||
const colormapLabel = document.getElementById('colormapLabel');
|
||||
if (numArrays > 0) {
|
||||
if (numArrays > 0 || numCellArrays > 0) {
|
||||
colormapSelector.innerHTML = '';
|
||||
|
||||
// Add popular colormaps first
|
||||
@@ -929,24 +950,54 @@
|
||||
if (fieldName && currentReader) {
|
||||
// Get the polydata with scalar field (from mapper's input, not reader)
|
||||
const polydata = mapper.getInputData();
|
||||
const pointData = polydata.getPointData();
|
||||
const scalarArray = pointData.getArrayByName(fieldName);
|
||||
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
pointData.setActiveScalars(fieldName);
|
||||
// Check if this is a cell data field (face attribute) or point data field (vertex attribute)
|
||||
if (fieldName.startsWith('cell:')) {
|
||||
// Handle cell data (face attributes)
|
||||
const actualFieldName = fieldName.substring(5); // Remove "cell:" prefix
|
||||
const cellData = polydata.getCellData();
|
||||
const scalarArray = cellData.getArrayByName(actualFieldName);
|
||||
|
||||
// Configure mapper for scalar coloring
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUsePointData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
cellData.setActiveScalars(actualFieldName);
|
||||
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
// Configure mapper for scalar coloring using cell data
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUseCellData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
|
||||
console.log(`[GeomPack VTK Fields Viewer] Scalar field '${fieldName}' activated, range:`, scalarArray.getRange());
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
|
||||
console.log(`[GeomPack VTK Fields Viewer] Face field '${actualFieldName}' activated, range:`, scalarArray.getRange());
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Fields Viewer] Face field '${actualFieldName}' not found`);
|
||||
}
|
||||
} else if (fieldName.startsWith('point:')) {
|
||||
// Handle point data (vertex attributes)
|
||||
const actualFieldName = fieldName.substring(6); // Remove "point:" prefix
|
||||
const pointData = polydata.getPointData();
|
||||
const scalarArray = pointData.getArrayByName(actualFieldName);
|
||||
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
pointData.setActiveScalars(actualFieldName);
|
||||
|
||||
// Configure mapper for scalar coloring using point data
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUsePointData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
|
||||
console.log(`[GeomPack VTK Fields Viewer] Vertex field '${actualFieldName}' activated, range:`, scalarArray.getRange());
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Fields Viewer] Vertex field '${actualFieldName}' not found`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Fields Viewer] Scalar field '${fieldName}' not found`);
|
||||
console.warn(`[GeomPack VTK Fields Viewer] Unknown field type: ${fieldName}`);
|
||||
}
|
||||
} else {
|
||||
// Disable scalar coloring, use solid color
|
||||
|
||||
+70
-19
@@ -575,15 +575,36 @@
|
||||
for (let i = 0; i < numArrays; i++) {
|
||||
const array = pointData.getArray(i);
|
||||
const arrayName = array.getName();
|
||||
console.log(`[GeomPack VTK Filters] Found scalar field: ${arrayName}`);
|
||||
console.log(`[GeomPack VTK Filters] Found point field: ${arrayName}`);
|
||||
const option = document.createElement('option');
|
||||
option.value = arrayName;
|
||||
option.textContent = arrayName;
|
||||
option.value = `point:${arrayName}`;
|
||||
option.textContent = `${arrayName} (vertex)`;
|
||||
scalarSelector.appendChild(option);
|
||||
}
|
||||
scalarLabel.style.display = 'flex';
|
||||
console.log('[GeomPack VTK Filters] Scalar field selector populated with', numArrays, 'fields');
|
||||
} else {
|
||||
console.log('[GeomPack VTK Filters] Point data selector populated with', numArrays, 'fields');
|
||||
}
|
||||
|
||||
// Check for scalar fields in cell data (face attributes)
|
||||
const cellData = polydata.getCellData();
|
||||
const numCellArrays = cellData.getNumberOfArrays();
|
||||
console.log('[GeomPack VTK Filters] Cell data arrays:', numCellArrays);
|
||||
|
||||
if (numCellArrays > 0) {
|
||||
for (let i = 0; i < numCellArrays; i++) {
|
||||
const array = cellData.getArray(i);
|
||||
const arrayName = array.getName();
|
||||
console.log(`[GeomPack VTK Filters] Found face field: ${arrayName}`);
|
||||
const option = document.createElement('option');
|
||||
option.value = `cell:${arrayName}`;
|
||||
option.textContent = `${arrayName} (face)`;
|
||||
scalarSelector.appendChild(option);
|
||||
}
|
||||
scalarLabel.style.display = 'flex';
|
||||
console.log('[GeomPack VTK Filters] Cell data selector populated with', numCellArrays, 'fields');
|
||||
}
|
||||
|
||||
if (numArrays === 0 && numCellArrays === 0) {
|
||||
scalarLabel.style.display = 'none';
|
||||
console.log('[GeomPack VTK Filters] No scalar fields found');
|
||||
}
|
||||
@@ -591,7 +612,7 @@
|
||||
// Populate colormap selector
|
||||
const colormapSelector = document.getElementById('colormapSelector');
|
||||
const colormapLabel = document.getElementById('colormapLabel');
|
||||
if (numArrays > 0) {
|
||||
if (numArrays > 0 || numCellArrays > 0) {
|
||||
colormapSelector.innerHTML = '';
|
||||
|
||||
// Add popular colormaps first
|
||||
@@ -1096,24 +1117,54 @@
|
||||
if (fieldName && currentReader) {
|
||||
// Get the polydata with scalar field (from mapper's input, not reader)
|
||||
const polydata = mapper.getInputData();
|
||||
const pointData = polydata.getPointData();
|
||||
const scalarArray = pointData.getArrayByName(fieldName);
|
||||
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
pointData.setActiveScalars(fieldName);
|
||||
// Check if this is a cell data field (face attribute) or point data field (vertex attribute)
|
||||
if (fieldName.startsWith('cell:')) {
|
||||
// Handle cell data (face attributes)
|
||||
const actualFieldName = fieldName.substring(5); // Remove "cell:" prefix
|
||||
const cellData = polydata.getCellData();
|
||||
const scalarArray = cellData.getArrayByName(actualFieldName);
|
||||
|
||||
// Configure mapper for scalar coloring
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUsePointData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
cellData.setActiveScalars(actualFieldName);
|
||||
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
// Configure mapper for scalar coloring using cell data
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUseCellData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
|
||||
console.log(`[GeomPack VTK Filters] Scalar field '${fieldName}' activated, range:`, scalarArray.getRange());
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
|
||||
console.log(`[GeomPack VTK Filters] Face field '${actualFieldName}' activated, range:`, scalarArray.getRange());
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Filters] Face field '${actualFieldName}' not found`);
|
||||
}
|
||||
} else if (fieldName.startsWith('point:')) {
|
||||
// Handle point data (vertex attributes)
|
||||
const actualFieldName = fieldName.substring(6); // Remove "point:" prefix
|
||||
const pointData = polydata.getPointData();
|
||||
const scalarArray = pointData.getArrayByName(actualFieldName);
|
||||
|
||||
if (scalarArray) {
|
||||
// Set the scalar array as active scalars
|
||||
pointData.setActiveScalars(actualFieldName);
|
||||
|
||||
// Configure mapper for scalar coloring using point data
|
||||
mapper.setScalarVisibility(true);
|
||||
mapper.setScalarModeToUsePointData();
|
||||
mapper.setScalarRange(scalarArray.getRange());
|
||||
|
||||
// Apply colormap using the updateColormap function
|
||||
updateColormap();
|
||||
|
||||
console.log(`[GeomPack VTK Filters] Vertex field '${actualFieldName}' activated, range:`, scalarArray.getRange());
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Filters] Vertex field '${actualFieldName}' not found`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`[GeomPack VTK Filters] Scalar field '${fieldName}' not found`);
|
||||
console.warn(`[GeomPack VTK Filters] Unknown field type: ${fieldName}`);
|
||||
}
|
||||
} else {
|
||||
// Disable scalar coloring, use solid color
|
||||
|
||||
Reference in New Issue
Block a user