- Basic 2D projection

This commit is contained in:
bklronin
2024-07-08 22:14:25 +02:00
parent 9daf263aad
commit 5ff48c0f5e
16 changed files with 967 additions and 826 deletions

View File

@@ -2,6 +2,7 @@ import math
import re
from copy import copy
import numpy as np
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
from PySide6.QtGui import QPainter, QPen, QColor
from PySide6.QtCore import Qt, QPoint, QPointF, Signal
@@ -47,6 +48,81 @@ class SketchWidget(QWidget):
def create_workplane(self):
self.wp = self.solv.create_2d_base()
def create_workplane_projected(self):
self.wp = self.solv.create_2d_base()
def create_proj_lines(self, lines):
"""Lines as orientation projected from the sketch"""
for line in lines:
for point in line:
x, y, z = point
point = self.solv.add_point_2d(x, y, self.wp)
relation_point = {} # Reinitialize the dictionary
handle_nr = self.get_handle_nr(str(point))
relation_point['handle_nr'] = handle_nr
relation_point['solv_handle'] = point
relation_point['ui_point'] = QPoint(x, y)
self.slv_points_main.append(relation_point)
print("points", self.slv_points_main)
print("lines", self.slv_lines_main)
print("lines", lines)
def find_duplicate_points_2d(self, edges):
points = []
seen = set()
duplicates = []
for edge in edges:
for point in edge:
# Extract only x and y coordinates
point_2d = (point[0], point[1])
if point_2d in seen:
if point_2d not in duplicates:
duplicates.append(point_2d)
else:
seen.add(point_2d)
points.append(point_2d)
return duplicates
def normal_to_quaternion(self, normal):
normal = np.array(normal)
#normal = normal / np.linalg.norm(normal)
axis = np.cross([0, 0, 1], normal)
if np.allclose(axis, 0):
axis = np.array([1, 0, 0])
else:
axis = axis / np.linalg.norm(axis) # Normalize the axis
angle = np.arccos(np.dot([0, 0, 1], normal))
qw = np.cos(angle / 2)
sin_half_angle = np.sin(angle / 2)
qx, qy, qz = axis * sin_half_angle # This will now work correctly
return qw, qx, qy, qz
def create_workplane_space(self, points, normal):
print("edges", points)
origin = self.find_duplicate_points_2d(points)
print(origin)
x, y = origin[0]
origin = QPoint(x, y)
origin_handle = self.get_handle_from_ui_point(origin)
qw, qx, qy, qz = self.normal_to_quaternion(normal)
slv_normal = self.solv.add_normal_3d(qw, qx, qy, qz)
self.wp = self.solv.add_work_plane(origin_handle, slv_normal)
print(self.wp)
def get_handle_nr(self, input_str: str) -> int:
# Define the regex pattern to extract the handle number
pattern = r"handle=(\d+)"

View File

@@ -5,6 +5,7 @@ import vtk
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Signal
from vtkmodules.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from vtkmodules.util.numpy_support import vtk_to_numpy, numpy_to_vtk
class VTKWidget(QtWidgets.QWidget):
@@ -12,6 +13,10 @@ class VTKWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.access_selected_points = []
self.selected_normal = None
self.selected_edges = []
self.cell_normals = None
self.vtk_widget = QVTKRenderWindowInteractor(self)
# Create layout and add VTK widget
@@ -26,12 +31,12 @@ class VTKWidget(QtWidgets.QWidget):
# Set up the camera
self.camera = self.renderer.GetActiveCamera()
self.camera.SetPosition(5, 5, 5)
self.camera.SetPosition(5, 5, 100)
self.camera.SetFocalPoint(0, 0, 0)
# Set up picking
self.picker = vtk.vtkCellPicker()
self.picker.SetTolerance(0.0005)
self.picker.SetTolerance(0.005)
# Create a mapper and actor for picked cells
self.picked_mapper = vtk.vtkDataSetMapper()
@@ -47,78 +52,109 @@ class VTKWidget(QtWidgets.QWidget):
# Add observer for mouse clicks
self.interactor.AddObserver("RightButtonPressEvent", self.on_click)
def create_cube_mesh(self):
cube_source = vtk.vtkCubeSource()
print(cube_source)
@staticmethod
def compute_normal_from_lines(line1, line2):
vec1 = line1[1] - line1[0]
vec2 = line2[1] - line2[0]
normal = np.cross(vec1, vec2)
print(normal)
normal = normal / np.linalg.norm(normal)
return normal
def load_interactor_mesh(self, edges):
# Create vtkPoints to store all points
points = vtk.vtkPoints()
# Create vtkCellArray to store the lines
lines = vtk.vtkCellArray()
for edge in edges:
# Add points for this edge
point_id1 = points.InsertNextPoint(edge[0])
point_id2 = points.InsertNextPoint(edge[1])
# Create a line using the point IDs
line = vtk.vtkLine()
line.GetPointIds().SetId(0, point_id1)
line.GetPointIds().SetId(1, point_id2)
# Add the line to the cell array
lines.InsertNextCell(line)
# Create vtkPolyData to store the geometry
polydata = vtk.vtkPolyData()
polydata.SetPoints(points)
polydata.SetLines(lines)
# Verify that the polydata is not empty
if polydata.GetNumberOfPoints() == 0 or polydata.GetNumberOfCells() == 0:
print("Error: PolyData is empty")
sys.exit(1)
# Create a mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(cube_source.GetOutputPort())
mapper.SetInputData(polydata)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(0.0, 0.0, 1.0) # Set color to red
actor.GetProperty().SetLineWidth(2) # Set line width
# Add the actor to the scene
self.renderer.AddActor(actor)
#self.renderer.SetBackground(0.1, 0.2, 0.4) # Set background color
def simplify_mesh(self, input_mesh, target_reduction):
# Create the quadric decimation filter
decimate = vtk.vtkDecimatePro()
decimate.SetInputData(input_mesh)
# Render and interact
# Force an update of the pipeline
# mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
# Set the reduction factor (0 to 1, where 1 means maximum reduction)
decimate.SetTargetReduction(target_reduction)
def render_from_points_direct_with_faces(self, vertices, faces):
points = vtk.vtkPoints()
for i in range(vertices.shape[0]):
points.InsertNextPoint(vertices[i])
# Optional: Preserve topology (if needed)
decimate.PreserveTopologyOn()
# Create a vtkCellArray to store the triangles
triangles = vtk.vtkCellArray()
for i in range(faces.shape[0]):
triangle = vtk.vtkTriangle()
triangle.GetPointIds().SetId(0, faces[i, 0])
triangle.GetPointIds().SetId(1, faces[i, 1])
triangle.GetPointIds().SetId(2, faces[i, 2])
triangles.InsertNextCell(triangle)
# Perform the decimation
decimate.Update()
# Create a polydata object
polydata = vtk.vtkPolyData()
polydata.SetPoints(points)
polydata.SetPolys(triangles)
return decimate.GetOutput()
# Calculate normals
normalGenerator = vtk.vtkPolyDataNormals()
normalGenerator.SetInputData(polydata)
normalGenerator.ComputePointNormalsOn()
normalGenerator.ComputeCellNormalsOn()
normalGenerator.Update()
def combine_coplanar_faces(self, input_polydata, tolerance=0.001):
# Clean the polydata to merge duplicate points
clean = vtk.vtkCleanPolyData()
clean.SetInputData(input_polydata)
clean.SetTolerance(tolerance)
clean.Update()
self.cell_normals = vtk_to_numpy(normalGenerator.GetOutput().GetCellData().GetNormals())
# Generate normals and merge coplanar polygons
normals = vtk.vtkPolyDataNormals()
normals.SetInputConnection(clean.GetOutputPort())
normals.SplittingOff() # Disable splitting of sharp edges
normals.ConsistencyOn() # Ensure consistent polygon ordering
normals.AutoOrientNormalsOn() # Automatically orient normals
normals.ComputePointNormalsOff() # We only need face normals
normals.ComputeCellNormalsOn() # Compute cell normals
normals.Update()
# Create a mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polydata)
return normals.GetOutput()
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1) # Set color (white in this case)
actor.GetProperty().EdgeVisibilityOn() # Show edges
actor.GetProperty().SetLineWidth(2) # Set line width
def poisson_reconstruction(self, points):
# Create a polydata object from points
point_polydata = vtk.vtkPolyData()
point_polydata.SetPoints(points)
# Create a surface reconstruction filter
surf = vtk.vtkSurfaceReconstructionFilter()
surf.SetInputData(point_polydata)
surf.Update()
# (assuming you have the original mesh mapper and actor set up)
self.renderer.AddActor(actor) # Add the original mesh actor
# Add the edge actor to the renderer
# Create a contour filter to extract the surface
cf = vtk.vtkContourFilter()
cf.SetInputConnection(surf.GetOutputPort())
cf.SetValue(0, 0.0)
cf.Update()
# Reverse normals
reverse = vtk.vtkReverseSense()
reverse.SetInputConnection(cf.GetOutputPort())
reverse.ReverseCellsOn()
reverse.ReverseNormalsOn()
reverse.Update()
return reverse.GetOutput()
def load_interactor_mesh(self, simp_mesh):
vertices, faces = simp_mesh
self.load_custom_mesh(vertices, faces)
# Force an update of the pipeline
#mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
def load_custom_mesh(self, vertices, faces):
### Load meshes by own module
@@ -154,277 +190,114 @@ class VTKWidget(QtWidgets.QWidget):
mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
def create_simplified_outline(self, polydata):
# 1. Extract the outer surface
surface_filter = vtk.vtkDataSetSurfaceFilter()
surface_filter.SetInputData(polydata)
surface_filter.Update()
# 2. Extract feature edges (only boundary edges)
feature_edges = vtk.vtkFeatureEdges()
feature_edges.SetInputConnection(surface_filter.GetOutputPort())
feature_edges.BoundaryEdgesOn()
feature_edges.FeatureEdgesOff()
feature_edges.NonManifoldEdgesOff()
feature_edges.ManifoldEdgesOff()
feature_edges.Update()
# 3. Clean the edges to merge duplicate points
cleaner = vtk.vtkCleanPolyData()
cleaner.SetInputConnection(feature_edges.GetOutputPort())
cleaner.Update()
# 4. Optional: Smooth the outline
smooth = vtk.vtkSmoothPolyDataFilter()
smooth.SetInputConnection(cleaner.GetOutputPort())
smooth.SetNumberOfIterations(15)
smooth.SetRelaxationFactor(0.1)
smooth.FeatureEdgeSmoothingOff()
smooth.BoundarySmoothingOn()
smooth.Update()
return smooth
def render_from_points_direct_with_faces(self, points):
# Create a vtkPoints object and store the points in it
vtk_points = vtk.vtkPoints()
for point in points:
vtk_points.InsertNextPoint(point)
# Create a vtkCellArray to store the triangles
triangles = vtk.vtkCellArray()
# Assuming points are organized as triplets forming triangles
for i in range(0, len(points), 3):
triangle = vtk.vtkTriangle()
triangle.GetPointIds().SetId(0, i)
triangle.GetPointIds().SetId(1, i + 1)
triangle.GetPointIds().SetId(2, i + 2)
triangles.InsertNextCell(triangle)
# Create a polydata object
polydata = vtk.vtkPolyData()
polydata.SetPoints(vtk_points)
polydata.SetPolys(triangles)
# Optional: Merge duplicate points
cleaner = vtk.vtkCleanPolyData()
cleaner.SetInputData(polydata)
cleaner.Update()
# Optional: Combine coplanar faces
normals = vtk.vtkPolyDataNormals()
normals.SetInputConnection(cleaner.GetOutputPort())
normals.SplittingOff()
normals.ConsistencyOn()
normals.AutoOrientNormalsOn()
normals.ComputePointNormalsOff()
normals.ComputeCellNormalsOn()
normals.Update()
# Create a mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(normals.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1) # Set color (white in this case)
actor.GetProperty().EdgeVisibilityOn() # Show edges
actor.GetProperty().SetLineWidth(2) # Set line width
feature_edges = self.create_simplified_outline(polydata)
# Create a mapper for the feature edges
edge_mapper = vtk.vtkPolyDataMapper()
# Already wiht output
edge_mapper.SetInputConnection(feature_edges.GetOutputPort())
# Create an actor for the feature edges
edge_actor = vtk.vtkActor()
edge_actor.SetMapper(edge_mapper)
# Set the properties of the edge actor
edge_actor.GetProperty().SetColor(1, 0, 0) # Set color (red in this case)
edge_actor.GetProperty().SetLineWidth(2) # Set line width
# Optionally, if you want to keep the original mesh visible:
# (assuming you have the original mesh mapper and actor set up)
self.renderer.AddActor(actor) # Add the original mesh actor
# Add the edge actor to the renderer
self.renderer.AddActor(edge_actor)
# Force an update of the pipeline
mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
# Print statistics
print(f"Original points: {len(points)}")
print(f"Number of triangles: {triangles.GetNumberOfCells()}")
print(f"Final number of points: {normals.GetOutput().GetNumberOfPoints()}")
print(f"Final number of cells: {normals.GetOutput().GetNumberOfCells()}")
def render_from_points_direct(self, points):
### Rendermethod for SDF mesh (output)
# Create a vtkPoints object and store the points in it
vtk_points = vtk.vtkPoints()
for point in points:
vtk_points.InsertNextPoint(point)
# Create a polydata object
point_polydata = vtk.vtkPolyData()
point_polydata.SetPoints(vtk_points)
# Surface reconstruction
surf = vtk.vtkSurfaceReconstructionFilter()
surf.SetInputData(point_polydata)
surf.Update()
# Create a contour filter to extract the surface
cf = vtk.vtkContourFilter()
cf.SetInputConnection(surf.GetOutputPort())
cf.SetValue(0, 0.0)
cf.Update()
# Reverse the normals
reverse = vtk.vtkReverseSense()
reverse.SetInputConnection(cf.GetOutputPort())
reverse.ReverseCellsOn()
reverse.ReverseNormalsOn()
reverse.Update()
# Get the reconstructed mesh
reconstructed_mesh = reverse.GetOutput()
"""# Simplify the mesh
target_reduction = 1 # Adjust this value as needed
simplified_mesh = self.simplify_mesh(reconstructed_mesh, target_reduction)
combinded_faces = self.combine_coplanar_faces(simplified_mesh, 0.001)"""
# Create a mapper and actor for the simplified mesh
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(reconstructed_mesh)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1) # Set color (white in this case)
actor.GetProperty().EdgeVisibilityOn() # Show edges
actor.GetProperty().SetLineWidth(2) # Set line width
# Add the actor to the renderer
self.renderer.AddActor(actor)
# Force an update of the pipeline
#mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
# Print statistics
print(f"Original points: {len(points)}")
print(
f"Reconstructed mesh: {reconstructed_mesh.GetNumberOfPoints()} points, {reconstructed_mesh.GetNumberOfCells()} cells")
"""print(
f"Simplified mesh: {simplified_mesh.GetNumberOfPoints()} points, {simplified_mesh.GetNumberOfCells()} cells")"""
def on_click(self, obj, event):
click_pos = self.interactor.GetEventPosition()
# Perform pick
self.picker.Pick(click_pos[0], click_pos[1], 0, self.renderer)
# Get picked cell
# Get picked cell ID
cell_id = self.picker.GetCellId()
if cell_id != -1:
print(f"Picked face ID: {cell_id}")
print(f"Picked cell ID: {cell_id}")
# Get the polydata and the picked cell
polydata = self.picker.GetActor().GetMapper().GetInput()
cell = polydata.GetCell(cell_id)
# Project2D
renderer = self.vtk_widget.GetRenderWindow().GetRenderers().GetFirstRenderer()
camera = renderer.GetActiveCamera()
# Ensure it's a line
if cell.GetCellType() == vtk.VTK_LINE:
# Get the two points of the line
point_id1 = cell.GetPointId(0)
point_id2 = cell.GetPointId(1)
# Get cell type
cell_type = cell.GetCellType()
print(f"Cell type: {cell_type}")
proj_point1 = polydata.GetPoint(point_id1)
proj_point2 = polydata.GetPoint(point_id2)
# Get points of the cell
points = cell.GetPoints()
num_points = points.GetNumberOfPoints()
print(f"Number of points in the cell: {num_points}")
self.access_selected_points.append((proj_point1, proj_point2))
vec_points = []
point1 = np.array(proj_point1)
point2 = np.array(proj_point2)
# Get coordinates of each point
for i in range(num_points):
point = points.GetPoint(i)
print(f"Point {i}: {point}")
vec_points.append(point)
print(f"Line starts at: {point1}")
print(f"Line ends at: {point2}")
# Get normal of the cell (if it's a polygon)
if cell_type == vtk.VTK_TRIANGLE:
normal = [0, 0, 0]
vtk.vtkPolygon.ComputeNormal(points, normal)
print(f"Face normal: {normal}")
# Store this line for later use if needed
self.selected_edges.append((point1, point2))
# Get cell data
cell_data = polydata.GetCellData()
if cell_data:
num_arrays = cell_data.GetNumberOfArrays()
print(f"Number of cell data arrays: {num_arrays}")
for i in range(num_arrays):
array = cell_data.GetArray(i)
array_name = array.GetName()
num_components = array.GetNumberOfComponents()
value = [0] * num_components
array.GetTuple(cell_id, value)
print(f"Cell data '{array_name}': {value}")
if len(self.selected_edges) == 2:
# Compute the normal from the two selected edges
edge1 = self.selected_edges[0][1] - self.selected_edges[0][0]
edge2 = self.selected_edges[1][1] - self.selected_edges[1][0]
self.selected_normal = np.cross(edge1, edge2)
self.selected_normal = self.selected_normal / np.linalg.norm(self.selected_normal)
print("Computed normal:", self.selected_normal)
# Get point data (average of all points in the cell)
point_data = polydata.GetPointData()
if point_data:
num_arrays = point_data.GetNumberOfArrays()
print(f"Number of point data arrays: {num_arrays}")
for i in range(num_arrays):
array = point_data.GetArray(i)
array_name = array.GetName()
num_components = array.GetNumberOfComponents()
avg_value = np.zeros(num_components)
for j in range(num_points):
point_id = cell.GetPointId(j)
value = [0] * num_components
array.GetTuple(point_id, value)
avg_value += np.array(value)
avg_value /= num_points
print(f"Average point data '{array_name}': {avg_value}")
# Create a transform for projection
transform = vtk.vtkTransform()
transform.Identity()
if num_points and cell_data:
face_orient = {'cell_data': cell_data, 'points': vec_points }
print(face_orient)
self.face_data.emit(face_orient)
# Compute rotation to align normal with Z-axis
z_axis = np.array([0, 0, 1])
rotation_axis = np.cross(self.selected_normal, z_axis)
rotation_angle = np.arccos(np.dot(self.selected_normal, z_axis)) * 180 / np.pi
# Highlight picked face (your existing code)
ids = vtk.vtkIdTypeArray()
ids.SetNumberOfComponents(1)
ids.InsertNextValue(cell_id)
if np.linalg.norm(rotation_axis) > 1e-6: # Avoid division by zero
transform.RotateWXYZ(rotation_angle, rotation_axis[0], rotation_axis[1], rotation_axis[2])
selection_node = vtk.vtkSelectionNode()
selection_node.SetFieldType(vtk.vtkSelectionNode.CELL)
selection_node.SetContentType(vtk.vtkSelectionNode.INDICES)
selection_node.SetSelectionList(ids)
# Apply scaling to flatten
transform.Scale(1, 1, 0)
selection = vtk.vtkSelection()
selection.AddNode(selection_node)
# Apply the inverse rotation to bring it back to original orientation
transform.RotateWXYZ(-rotation_angle, rotation_axis[0], rotation_axis[1], rotation_axis[2])
extract_selection = vtk.vtkExtractSelection()
extract_selection.SetInputData(0, polydata)
extract_selection.SetInputData(1, selection)
extract_selection.Update()
# Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputData(polydata)
transformFilter.SetTransform(transform)
transformFilter.Update()
self.picked_mapper.SetInputData(extract_selection.GetOutput())
self.vtk_widget.GetRenderWindow().Render()
# Get the projected polydata
projected_polydata = transformFilter.GetOutput()
# Create a mapper and actor for the projected data
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(projected_polydata)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(0.0, 1.0, 0.0) # Set color to green
actor.GetProperty().SetLineWidth(4) # Set line width
# Add the actor to the scene
self.renderer.AddActor(actor)
# Add a plane to visualize the projection plane
plane_source = vtk.vtkPlaneSource()
plane_source.SetCenter(0, 0, 0)
plane_source.SetNormal(self.selected_normal)
plane_mapper = vtk.vtkPolyDataMapper()
plane_mapper.SetInputConnection(plane_source.GetOutputPort())
plane_actor = vtk.vtkActor()
plane_actor.SetMapper(plane_mapper)
plane_actor.GetProperty().SetColor(0.8, 0.8, 0.8) # Light gray
plane_actor.GetProperty().SetOpacity(0.5)
self.renderer.AddActor(plane_actor)
# Reset camera to show all actors
self.renderer.ResetCamera()
# Render and interact
self.vtk_widget.GetRenderWindow().Render()
# Reset selected edges
self.selected_edges = []
elif len(self.access_selected_points) > 2:
self.access_selected_points = []
else:
print("Selected cell is not a line")
def start(self):
self.interactor.Initialize()
self.interactor.Start()

View File

@@ -0,0 +1,337 @@
def are_coplanar(self, normal1, normal2, point1, point2, tolerance=1e-6):
# Check if normals are parallel
if np.abs(np.dot(normal1, normal2)) < 1 - tolerance:
return False
# Check if points lie on the same plane
diff = point2 - point1
return np.abs(np.dot(diff, normal1)) < tolerance
def merge_coplanar_triangles(self, polydata):
# Compute normals
normalGenerator = vtk.vtkPolyDataNormals()
normalGenerator.SetInputData(polydata)
normalGenerator.ComputePointNormalsOff()
normalGenerator.ComputeCellNormalsOn()
normalGenerator.Update()
mesh = normalGenerator.GetOutput()
n_cells = mesh.GetNumberOfCells()
# Create a map to store merged triangles
merged = {}
for i in range(n_cells):
if i in merged:
continue
cell = mesh.GetCell(i)
normal = np.array(mesh.GetCellData().GetNormals().GetTuple(i))
point = np.array(cell.GetPoints().GetPoint(0))
merged[i] = [i]
for j in range(i + 1, n_cells):
if j in merged:
continue
cell_j = mesh.GetCell(j)
normal_j = np.array(mesh.GetCellData().GetNormals().GetTuple(j))
point_j = np.array(cell_j.GetPoints().GetPoint(0))
if self.are_coplanar(normal, normal_j, point, point_j):
merged[i].append(j)
# Create new polygons
new_polygons = vtk.vtkCellArray()
for group in merged.values():
if len(group) > 1:
polygon = vtk.vtkPolygon()
points = set()
for idx in group:
cell = mesh.GetCell(idx)
for j in range(3):
point_id = cell.GetPointId(j)
points.add(point_id)
polygon.GetPointIds().SetNumberOfIds(len(points))
for j, point_id in enumerate(points):
polygon.GetPointIds().SetId(j, point_id)
new_polygons.InsertNextCell(polygon)
else:
new_polygons.InsertNextCell(mesh.GetCell(group[0]))
# Create new polydata
new_polydata = vtk.vtkPolyData()
new_polydata.SetPoints(mesh.GetPoints())
new_polydata.SetPolys(new_polygons)
return new_polydata
def create_cube_mesh(self):
# cube_source = vtk.vtkSuperquadricSource()
reader = vtk.vtkSTLReader()
reader.SetFileName("case.stl") # Replace with your mesh file path
reader.Update()
featureEdges = vtk.vtkFeatureEdges()
featureEdges.SetInputConnection(reader.GetOutputPort())
featureEdges.BoundaryEdgesOn()
featureEdges.FeatureEdgesOn()
featureEdges.ManifoldEdgesOff()
featureEdges.NonManifoldEdgesOff()
featureEdges.Update()
# print(cube_source)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(reader.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
self.renderer.AddActor(actor)
mapper_edge = vtk.vtkPolyDataMapper()
mapper_edge.SetInputConnection(featureEdges.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper_edge)
self.renderer.AddActor(actor)
def simplify_mesh(self, input_mesh, target_reduction):
# Create the quadric decimation filter
decimate = vtk.vtkDecimatePro()
decimate.SetInputData(input_mesh)
# Set the reduction factor (0 to 1, where 1 means maximum reduction)
decimate.SetTargetReduction(target_reduction)
# Optional: Preserve topology (if needed)
decimate.PreserveTopologyOn()
# Perform the decimation
decimate.Update()
return decimate.GetOutput()
def combine_coplanar_faces(self, input_polydata, tolerance=0.001):
# Clean the polydata to merge duplicate points
clean = vtk.vtkCleanPolyData()
clean.SetInputData(input_polydata)
clean.SetTolerance(tolerance)
clean.Update()
# Generate normals and merge coplanar polygons
normals = vtk.vtkPolyDataNormals()
normals.SetInputConnection(clean.GetOutputPort())
normals.SplittingOff() # Disable splitting of sharp edges
normals.ConsistencyOn() # Ensure consistent polygon ordering
normals.AutoOrientNormalsOn() # Automatically orient normals
normals.ComputePointNormalsOff() # We only need face normals
normals.ComputeCellNormalsOn() # Compute cell normals
normals.Update()
return normals.GetOutput()
def poisson_reconstruction(self, points):
# Create a polydata object from points
point_polydata = vtk.vtkPolyData()
point_polydata.SetPoints(points)
# Create a surface reconstruction filter
surf = vtk.vtkSurfaceReconstructionFilter()
surf.SetInputData(point_polydata)
surf.Update()
# Create a contour filter to extract the surface
cf = vtk.vtkContourFilter()
cf.SetInputConnection(surf.GetOutputPort())
cf.SetValue(0, 0.0)
cf.Update()
# Reverse normals
reverse = vtk.vtkReverseSense()
reverse.SetInputConnection(cf.GetOutputPort())
reverse.ReverseCellsOn()
reverse.ReverseNormalsOn()
reverse.Update()
return reverse.GetOutput()
def create_simplified_outline(self, polydata):
featureEdges = vtk.vtkFeatureEdges()
featureEdges.SetInputData(polydata)
featureEdges.BoundaryEdgesOn()
featureEdges.FeatureEdgesOn()
featureEdges.ManifoldEdgesOff()
featureEdges.NonManifoldEdgesOff()
featureEdges.Update()
"""# 3. Clean the edges to merge duplicate points
cleaner = vtk.vtkCleanPolyData()
cleaner.SetInputConnection(feature_edges.GetOutputPort())
cleaner.Update()
# 4. Optional: Smooth the outline
smooth = vtk.vtkSmoothPolyDataFilter()
smooth.SetInputConnection(cleaner.GetOutputPort())
smooth.SetNumberOfIterations(15)
smooth.SetRelaxationFactor(0.1)
smooth.FeatureEdgeSmoothingOff()
smooth.BoundarySmoothingOn()
smooth.Update()"""
return featureEdges
def render_from_points_direct_with_faces(self, vertices, faces):
points = vtk.vtkPoints()
for i in range(vertices.shape[0]):
points.InsertNextPoint(vertices[i])
# Create a vtkCellArray to store the triangles
triangles = vtk.vtkCellArray()
for i in range(faces.shape[0]):
triangle = vtk.vtkTriangle()
triangle.GetPointIds().SetId(0, faces[i, 0])
triangle.GetPointIds().SetId(1, faces[i, 1])
triangle.GetPointIds().SetId(2, faces[i, 2])
triangles.InsertNextCell(triangle)
"""vtk_points = vtk.vtkPoints()
for point in points:
vtk_points.InsertNextPoint(point)
# Create a vtkCellArray to store the triangles
triangles = vtk.vtkCellArray()
# Assuming points are organized as triplets forming triangles
for i in range(0, len(points), 3):
triangle = vtk.vtkTriangle()
triangle.GetPointIds().SetId(0, i)
triangle.GetPointIds().SetId(1, i + 1)
triangle.GetPointIds().SetId(2, i + 2)
triangles.InsertNextCell(triangle)"""
# Create a polydata object
polydata = vtk.vtkPolyData()
polydata.SetPoints(points)
polydata.SetPolys(triangles)
# Calculate normals
normalGenerator = vtk.vtkPolyDataNormals()
normalGenerator.SetInputData(polydata)
normalGenerator.ComputePointNormalsOn()
normalGenerator.ComputeCellNormalsOn()
normalGenerator.Update()
self.cell_normals = vtk_to_numpy(normalGenerator.GetOutput().GetCellData().GetNormals())
# merged_polydata = self.merge_coplanar_triangles(polydata)
# Create a mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polydata)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1) # Set color (white in this case)
actor.GetProperty().EdgeVisibilityOn() # Show edges
actor.GetProperty().SetLineWidth(2) # Set line width
feature_edges = self.create_simplified_outline(polydata)
# Create a mapper for the feature edges
edge_mapper = vtk.vtkPolyDataMapper()
# Already wiht output
edge_mapper.SetInputConnection(feature_edges.GetOutputPort())
# Create an actor for the feature edges
edge_actor = vtk.vtkActor()
edge_actor.SetMapper(edge_mapper)
# Set the properties of the edge actor
edge_actor.GetProperty().SetColor(1, 0, 0) # Set color (red in this case)
edge_actor.GetProperty().SetLineWidth(2) # Set line width
# Optionally, if you want to keep the original mesh visible:
# (assuming you have the original mesh mapper and actor set up)
self.renderer.AddActor(actor) # Add the original mesh actor
# Add the edge actor to the renderer
self.renderer.AddActor(edge_actor)
# Force an update of the pipeline
mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
"""# Print statistics
print(f"Original points: {len(points)}")
print(f"Number of triangles: {triangles.GetNumberOfCells()}")
print(f"Final number of points: {normals.GetOutput().GetNumberOfPoints()}")
print(f"Final number of cells: {normals.GetOutput().GetNumberOfCells()}")"""
def render_from_points_direct(self, points):
### Rendermethod for SDF mesh (output)
# Create a vtkPoints object and store the points in it
vtk_points = vtk.vtkPoints()
for point in points:
vtk_points.InsertNextPoint(point)
# Create a polydata object
point_polydata = vtk.vtkPolyData()
point_polydata.SetPoints(vtk_points)
# Surface reconstruction
surf = vtk.vtkSurfaceReconstructionFilter()
surf.SetInputData(point_polydata)
surf.Update()
# Create a contour filter to extract the surface
cf = vtk.vtkContourFilter()
cf.SetInputConnection(surf.GetOutputPort())
cf.SetValue(0, 0.0)
cf.Update()
# Reverse the normals
reverse = vtk.vtkReverseSense()
reverse.SetInputConnection(cf.GetOutputPort())
reverse.ReverseCellsOn()
reverse.ReverseNormalsOn()
reverse.Update()
# Get the reconstructed mesh
reconstructed_mesh = reverse.GetOutput()
"""# Simplify the mesh
target_reduction = 1 # Adjust this value as needed
simplified_mesh = self.simplify_mesh(reconstructed_mesh, target_reduction)
combinded_faces = self.combine_coplanar_faces(simplified_mesh, 0.001)"""
# Create a mapper and actor for the simplified mesh
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(reconstructed_mesh)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 1, 1) # Set color (white in this case)
actor.GetProperty().EdgeVisibilityOn() # Show edges
actor.GetProperty().SetLineWidth(2) # Set line width
# Add the actor to the renderer
self.renderer.AddActor(actor)
# Force an update of the pipeline
# mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
# Print statistics
print(f"Original points: {len(points)}")
print(
f"Reconstructed mesh: {reconstructed_mesh.GetNumberOfPoints()} points, {reconstructed_mesh.GetNumberOfCells()} cells")
"""print(
f"Simplified mesh: {simplified_mesh.GetNumberOfPoints()} points, {simplified_mesh.GetNumberOfCells()} cells")"""