- Basic 2D projection
This commit is contained in:
@@ -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+)"
|
||||
|
||||
@@ -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()
|
||||
|
||||
337
drawing_modules/vtk_widget_alt_methods.py
Normal file
337
drawing_modules/vtk_widget_alt_methods.py
Normal 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")"""
|
||||
Reference in New Issue
Block a user