Fixed interactor

Added proj lines selected
This commit is contained in:
bklronin 2024-07-17 16:53:25 +02:00
parent 048ace83ce
commit a8d15d7b4b
4 changed files with 185 additions and 60 deletions

View File

@ -5,7 +5,7 @@ from copy import copy
import numpy as np import numpy as np
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
from PySide6.QtGui import QPainter, QPen, QColor, QTransform from PySide6.QtGui import QPainter, QPen, QColor, QTransform
from PySide6.QtCore import Qt, QPoint, QPointF, Signal from PySide6.QtCore import Qt, QPoint, QPointF, Signal, QLine
from python_solvespace import SolverSystem, ResultFlag from python_solvespace import SolverSystem, ResultFlag
@ -51,13 +51,11 @@ class SketchWidget(QWidget):
def create_workplane_projected(self): def create_workplane_projected(self):
self.wp = self.solv.create_2d_base() self.wp = self.solv.create_2d_base()
def create_proj_lines(self, lines): def create_proj_points(self, proj_points):
"""Lines as orientation projected from the sketch""" """Lines as orientation projected from the sketch"""
for point in lines: for point in proj_points:
x, y = point x, y = point
self.proj_snap_lines = lines
# Invert X from projection might be happening in the projection for some reason Careful
coord = QPoint(x, y) coord = QPoint(x, y)
self.proj_snap_points.append(coord) self.proj_snap_points.append(coord)
@ -69,6 +67,24 @@ class SketchWidget(QWidget):
self.slv_points_main.append(relation_point)""" self.slv_points_main.append(relation_point)"""
def create_proj_lines(self, sel_edges):
"""Lines as orientation projected from the sketch"""
print("Incoming corrd lines", sel_edges)
for line in sel_edges:
start = QPoint(line[0][0], line[0][1] )
end = QPoint(line[1][0], line[1][1])
coord = QLine(start, end)
self.proj_snap_lines.append(coord)
"""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)"""
def find_duplicate_points_2d(self, edges): def find_duplicate_points_2d(self, edges):
points = [] points = []
seen = set() seen = set()
@ -675,11 +691,11 @@ class SketchWidget(QWidget):
return QPoint(int(widget_x), int(widget_y)) return QPoint(int(widget_x), int(widget_y))
def from_quadrant_coords_no_center(self, point): def from_quadrant_coords_no_center(self, point):
"""Translate quadrant coordinates to linear coordinates.""" """Invert Y Coordinate for mesh"""
center_x = 0 center_x = 0
center_y = 0 center_y = 0
widget_x = center_x + point.x() * self.zoom widget_x = point.x()
widget_y = center_y - point.y() * self.zoom # Note the subtraction here widget_y = -point.y()
return QPoint(int(widget_x), int(widget_y)) return QPoint(int(widget_x), int(widget_y))
def paintEvent(self, event): def paintEvent(self, event):
@ -746,6 +762,11 @@ class SketchWidget(QWidget):
for cross in self.proj_snap_points: for cross in self.proj_snap_points:
self.draw_cross(painter, cross, 10 / self.zoom) self.draw_cross(painter, cross, 10 / self.zoom)
for selected in self.proj_snap_lines:
pen = QPen(Qt.white, 1, Qt.DashLine)
painter.setPen(pen)
painter.drawLine(selected)
painter.end() painter.end()
def wheelEvent(self, event): def wheelEvent(self, event):

View File

@ -13,6 +13,7 @@ class VTKWidget(QtWidgets.QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.selected_vtk_line = []
self.access_selected_points = [] self.access_selected_points = []
self.selected_normal = None self.selected_normal = None
self.centroid = None self.centroid = None
@ -21,7 +22,8 @@ class VTKWidget(QtWidgets.QWidget):
self.local_matrix = None self.local_matrix = None
self.project_tosketch_edge = [] self.project_tosketch_points = []
self.project_tosketch_lines = []
self.vtk_widget = QVTKRenderWindowInteractor(self) self.vtk_widget = QVTKRenderWindowInteractor(self)
@ -112,10 +114,12 @@ class VTKWidget(QtWidgets.QWidget):
mapper.SetInputData(grid) mapper.SetInputData(grid)
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetPickable(False)
actor.SetMapper(mapper) actor.SetMapper(mapper)
actor.GetProperty().SetColor(0.5, 0.5, 0.5) # Set grid color to gray actor.GetProperty().SetColor(0.5, 0.5, 0.5) # Set grid color to gray
self.renderer.AddActor(actor) self.renderer.AddActor(actor)
def create_grid(self, size=100, spacing=10): def create_grid(self, size=100, spacing=10):
# Create a vtkPoints object and store the points in it # Create a vtkPoints object and store the points in it
points = vtk.vtkPoints() points = vtk.vtkPoints()
@ -169,7 +173,7 @@ class VTKWidget(QtWidgets.QWidget):
normal = normal / np.linalg.norm(normal) normal = normal / np.linalg.norm(normal)
return normal return normal
def load_interactor_mesh(self, edges): def load_interactor_mesh(self, edges, off_vector):
# Create vtkPoints to store all points # Create vtkPoints to store all points
points = vtk.vtkPoints() points = vtk.vtkPoints()
@ -195,28 +199,34 @@ class VTKWidget(QtWidgets.QWidget):
polydata.SetLines(lines) polydata.SetLines(lines)
# Create a transform for mirroring across the y-axis # Create a transform for mirroring across the y-axis
mirror_transform = vtk.vtkTransform() matrix_transform = vtk.vtkTransform()
"""if self.local_matrix: if self.local_matrix:
print(self.local_matrix) print(self.local_matrix)
matrix = vtk.vtkMatrix4x4() matrix = vtk.vtkMatrix4x4()
matrix.DeepCopy(self.local_matrix) matrix.DeepCopy(self.local_matrix)
matrix.Invert() matrix.Invert()
mirror_transform.SetMatrix(matrix) matrix_transform.SetMatrix(matrix)
#matrix_transform.Scale(1, 1, 1) # This mirrors across the y-axis
mirror_transform.Scale(-1, -1, 1) # Inverting the original mirror look down # Apply the matrix transform
mirror_transform.Scale(1, 1, 1) # This mirrors across the y-axis"""
# Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter() transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputData(polydata) transformFilter.SetInputData(polydata)
transformFilter.SetTransform(mirror_transform) transformFilter.SetTransform(matrix_transform)
transformFilter.Update() transformFilter.Update()
# Create and apply the offset transform
offset_transform = vtk.vtkTransform()
offset_transform.Translate(off_vector[0], off_vector[1], off_vector[2])
offsetFilter = vtk.vtkTransformPolyDataFilter()
offsetFilter.SetInputConnection(transformFilter.GetOutputPort())
offsetFilter.SetTransform(offset_transform)
offsetFilter.Update()
# Create a mapper and actor # Create a mapper and actor
mapper = vtk.vtkPolyDataMapper() mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(transformFilter.GetOutput()) mapper.SetInputConnection(offsetFilter.GetOutputPort())
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetMapper(mapper) actor.SetMapper(mapper)
@ -270,6 +280,9 @@ class VTKWidget(QtWidgets.QWidget):
actor.GetProperty().SetColor(color) actor.GetProperty().SetColor(color)
actor.GetProperty().EdgeVisibilityOff() actor.GetProperty().EdgeVisibilityOff()
actor.GetProperty().SetLineWidth(line_width) actor.GetProperty().SetLineWidth(line_width)
actor.GetProperty().SetMetallic(1)
actor.GetProperty().SetOpacity(0.8)
actor.SetPickable(False)
self.renderer.AddActor(actor) self.renderer.AddActor(actor)
self.body_actors_orig.append(actor) self.body_actors_orig.append(actor)
@ -385,6 +398,62 @@ class VTKWidget(QtWidgets.QWidget):
return xy_coordinates return xy_coordinates
def compute_2d_coordinates_line(self, line_cell, normal):
# Ensure the input is a vtkLine
if not isinstance(line_cell, vtk.vtkLine):
raise ValueError("Input must be a vtkLine cell")
# Normalize the normal vector
normal = np.array(normal)
normal = normal / np.linalg.norm(normal)
# Create a vtkTransform
transform = vtk.vtkTransform()
transform.PostMultiply() # This ensures transforms are applied in the order we specify
# Rotate so that the normal aligns with the Z-axis
rotation_axis = np.cross(normal, [0, 0, 1])
angle = np.arccos(np.dot(normal, [0, 0, 1])) * 180 / np.pi # Convert to degrees
if np.linalg.norm(rotation_axis) > 1e-6: # Check if rotation is needed
transform.RotateWXYZ(angle, rotation_axis[0], rotation_axis[1], rotation_axis[2])
# Get the transformation matrix
matrix = transform.GetMatrix()
local_matrix = [matrix.GetElement(i, j) for i in range(4) for j in range(4)]
# Create a vtkPoints object to store the line points
points = vtk.vtkPoints()
points.InsertNextPoint(line_cell.GetPoints().GetPoint(0))
points.InsertNextPoint(line_cell.GetPoints().GetPoint(1))
# Create a vtkPolyData to represent the line
polydata = vtk.vtkPolyData()
polydata.SetPoints(points)
# Create a vtkCellArray to store the line cell
cells = vtk.vtkCellArray()
cells.InsertNextCell(line_cell)
polydata.SetLines(cells)
# Apply the transform to the polydata
transform_filter = vtk.vtkTransformPolyDataFilter()
transform_filter.SetInputData(polydata)
transform_filter.SetTransform(transform)
transform_filter.Update()
# Get the transformed points
transformed_polydata = transform_filter.GetOutput()
transformed_points = transformed_polydata.GetPoints()
# Extract 2D coordinates
xy_coordinates = []
for i in range(transformed_points.GetNumberOfPoints()):
point = transformed_points.GetPoint(i)
xy_coordinates.append((point[0], point[1]))
return xy_coordinates
def project_2d_to_3d(self, xy_coordinates, normal): def project_2d_to_3d(self, xy_coordinates, normal):
# Normalize the normal vector # Normalize the normal vector
normal = np.array(normal) normal = np.array(normal)
@ -494,6 +563,8 @@ class VTKWidget(QtWidgets.QWidget):
# Ensure it's a line # Ensure it's a line
if cell.GetCellType() == vtk.VTK_LINE: if cell.GetCellType() == vtk.VTK_LINE:
self.selected_vtk_line.append(cell)
# Get the two points of the line # Get the two points of the line
point_id1 = cell.GetPointId(0) point_id1 = cell.GetPointId(0)
point_id2 = cell.GetPointId(1) point_id2 = cell.GetPointId(1)
@ -533,9 +604,11 @@ class VTKWidget(QtWidgets.QWidget):
if len(self.selected_edges) == 2: if len(self.selected_edges) == 2:
self.compute_projection(False) self.compute_projection(False)
elif len(self.selected_edges) > 2: if len(self.selected_edges) > 2:
del self.selected_edges[0] self.selected_vtk_line.clear()
pass self.selected_edges.clear()
self.clear_actors_projection()
self.clear_edge_select()
def find_origin_vertex(self, edge1, edge2): def find_origin_vertex(self, edge1, edge2):
if edge1[0] == edge2[0]or edge1[0] == edge2[1]: if edge1[0] == edge2[0]or edge1[0] == edge2[1]:
@ -559,6 +632,7 @@ class VTKWidget(QtWidgets.QWidget):
self.renderer.RemoveActor(normals) self.renderer.RemoveActor(normals)
def compute_projection(self, direction_invert: bool= False): def compute_projection(self, direction_invert: bool= False):
# Compute the normal from the two selected edges ) # Compute the normal from the two selected edges )
edge1 = self.selected_edges[0][1] - self.selected_edges[0][0] edge1 = self.selected_edges[0][1] - self.selected_edges[0][0]
edge2 = self.selected_edges[1][1] - self.selected_edges[1][0] edge2 = self.selected_edges[1][1] - self.selected_edges[1][0]
@ -583,11 +657,14 @@ class VTKWidget(QtWidgets.QWidget):
polydata = self.picker.GetActor().GetMapper().GetInput() polydata = self.picker.GetActor().GetMapper().GetInput()
projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, self.centroid) projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, self.centroid)
projected_points = projected_polydata.GetPoints()
#print("proj_points", projected_points)
# Extract 2D coordinates # Extract 2D coordinates
self.project_tosketch_edge = self.compute_2d_coordinates(projected_polydata, self.selected_normal) self.project_tosketch_points = self.compute_2d_coordinates(projected_polydata, self.selected_normal)
# Seperately rotate selected edges for drawing
for vtk_line in self.selected_vtk_line:
proj_vtk_line = self.compute_2d_coordinates_line(vtk_line, self.selected_normal)
self.project_tosketch_lines.append(proj_vtk_line)
# Create a mapper and actor for the projected data # Create a mapper and actor for the projected data
mapper = vtk.vtkPolyDataMapper() mapper = vtk.vtkPolyDataMapper()
@ -595,13 +672,14 @@ class VTKWidget(QtWidgets.QWidget):
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetMapper(mapper) actor.SetMapper(mapper)
#actor.GetProperty().SetRenderLinesAsTubes(True)
actor.GetProperty().SetColor(0.0, 1.0, 0.0) # Set color to green actor.GetProperty().SetColor(0.0, 1.0, 0.0) # Set color to green
actor.GetProperty().SetLineWidth(4) # Set line width actor.GetProperty().SetLineWidth(8) # Set line width
self.renderer.AddActor(normal_actor) self.renderer.AddActor(normal_actor)
self.displayed_normal_actors.append(normal_actor) self.displayed_normal_actors.append(normal_actor)
# Add the actor to the scene # Add the edge lines actor to the scene
self.renderer.AddActor(actor) self.renderer.AddActor(actor)
self.picked_edge_actors.append(actor) self.picked_edge_actors.append(actor)

87
main.py
View File

@ -133,11 +133,14 @@ class MainWindow(QMainWindow):
def add_new_sketch_wp(self): def add_new_sketch_wp(self):
self.sketchWidget.clear_sketch() self.sketchWidget.clear_sketch()
#edges = [((-158.0, -20.0, -25.0), (286.0, -195.0, -25.0)), ((-158.0, -20.0, 25.0), (-158.0, -20.0, -25.0))] #edges = [((-158.0, -20.0, -25.0), (286.0, -195.0, -25.0)), ((-158.0, -20.0, 25.0), (-158.0, -20.0, -25.0))]
edges = self.custom_3D_Widget.project_tosketch_edge points = self.custom_3D_Widget.project_tosketch_points
normal = self.custom_3D_Widget.selected_normal normal = self.custom_3D_Widget.selected_normal
selected_lines = self.custom_3D_Widget.project_tosketch_lines
print("Selected lines", selected_lines)
self.sketchWidget.create_workplane_projected() self.sketchWidget.create_workplane_projected()
self.sketchWidget.create_proj_lines(edges) self.sketchWidget.create_proj_points(points)
self.sketchWidget.create_proj_lines(selected_lines)
# CLear all selections after it has been projected # CLear all selections after it has been projected
#self.custom_3D_Widget.clear_edge_select() #self.custom_3D_Widget.clear_edge_select()
@ -377,7 +380,6 @@ class MainWindow(QMainWindow):
selected = self.ui.sketch_list.currentItem() selected = self.ui.sketch_list.currentItem()
name = selected.text() name = selected.text()
points = self.model['sketch'][name]['sketch_points'] points = self.model['sketch'][name]['sketch_points']
lines = self.convert_lines_for_interactor()
if points[-1] == points[0]: if points[-1] == points[0]:
#detect loop that causes problems in mesh generation #detect loop that causes problems in mesh generation
@ -391,12 +393,8 @@ class MainWindow(QMainWindow):
length = 0 length = 0
print("Extrude cancelled") print("Extrude cancelled")
#Create and draw Interactor
geo = Geometry() geo = Geometry()
# Rotation is done in vtk matrix trans
angle = 0
normal = self.custom_3D_Widget.selected_normal normal = self.custom_3D_Widget.selected_normal
print("Normie enter", normal) print("Normie enter", normal)
if normal is None: if normal is None:
@ -407,20 +405,9 @@ class MainWindow(QMainWindow):
centroid = [0, 0, 0] centroid = [0, 0, 0]
else: else:
centroid = list(centroid) centroid = list(centroid)
print("THis centroid ",centroid) print("This centroid ", centroid)
f = geo.extrude_shape(points, length, angle, normal, centroid, is_symmetric, invert) f = geo.extrude_shape(points, length, normal, centroid, is_symmetric, invert, 0)
z_origin = centroid[2]
if is_symmetric:
z_origin = z_origin - length / 2
if invert:
edges = interactor_mesh.generate_mesh(lines, z_origin, length, True)
else:
edges = interactor_mesh.generate_mesh(lines, z_origin, length, False)
self.custom_3D_Widget.load_interactor_mesh(edges)
name_op = f"extrd-{name}" name_op = f"extrd-{name}"
element = { element = {
@ -428,7 +415,18 @@ class MainWindow(QMainWindow):
'type': 'extrude', 'type': 'extrude',
'sdf_object': f, 'sdf_object': f,
} }
#print(element)
### Interactor
lines = self.convert_lines_for_interactor()
edges = interactor_mesh.generate_mesh(lines, 0, length)
offset_vector = geo.vector_to_centroid(None, centroid, normal)
#print("off_ved", offset_vector)
if len(offset_vector) == 0 :
offset_vector = [0, 0, 0]
self.custom_3D_Widget.load_interactor_mesh(edges, offset_vector)
self.model['operation'][name_op] = element self.model['operation'][name_op] = element
self.ui.body_list.addItem(name_op) self.ui.body_list.addItem(name_op)
@ -500,7 +498,22 @@ class Geometry:
print("p2", p2) print("p2", p2)
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
def extrude_shape(self, points, length: float, angle, normal, centroid, symet: bool = True, invert: bool = False): def vector_to_centroid(self, shape_center, centroid, normal):
if not shape_center:
# Calculate the current center of the shape
shape_center = [0, 0, 0]
# Calculate the vector from the shape's center to the centroid
center_to_centroid = np.array(centroid) - np.array(shape_center)
# Project this vector onto the normal to get the required translation along the normal
translation_along_normal = np.dot(center_to_centroid, normal) * normal
return translation_along_normal
def extrude_shape(self, points, length: float, normal, centroid, symet: bool = True, invert: bool = False,
offset_length: float = None):
""" """
Extrude a 2D shape into 3D, orient it along the normal, and position it relative to the centroid. Extrude a 2D shape into 3D, orient it along the normal, and position it relative to the centroid.
""" """
@ -520,17 +533,29 @@ class Geometry:
# Orient the shape along the normal vector # Orient the shape along the normal vector
f = f.orient(normal) f = f.orient(normal)
# Calculate the current center of the shape offset_vector = self.vector_to_centroid(None, centroid, normal)
shape_center = [0,0,0] # Adjust the offset vector by subtracting the inset distance along the normal direction
adjusted_offset = offset_vector - (normal * length)
if invert:
# Translate the shape along the adjusted offset vector
f = f.translate(adjusted_offset)
else:
f = f.translate(offset_vector)
# Calculate the vector from the shape's center to the centroid # If offset_length is provided, adjust the offset_vector
center_to_centroid = np.array(centroid) - np.array(shape_center) if offset_length is not None:
# Check if offset_vector is not a zero vector
offset_vector_magnitude = np.linalg.norm(offset_vector)
if offset_vector_magnitude > 1e-10: # Use a small threshold to avoid floating-point issues
# Normalize the offset vector
offset_vector_norm = offset_vector / offset_vector_magnitude
# Scale the normalized vector by the desired length
offset_vector = offset_vector_norm * offset_length
f = f.translate(offset_vector)
else:
print("Warning: Offset vector has zero magnitude. Using original vector.")
# Project this vector onto the normal to get the required translation along the normal # Translate the shape along the adjusted offset vector
translation_along_normal = np.dot(center_to_centroid, normal) * normal
# Translate the shape along the normal
f = f.translate(translation_along_normal)
return f return f

View File

@ -1,8 +1,9 @@
# Draw simple boundary based on the lines and depth # Draw simple boundary based on the lines and depth
def generate_mesh(lines: list, z_origin: float, depth: float, invert :bool = False): def generate_mesh(lines: list, z_origin: float, depth: float, invert: bool = False):
origin = create_3D(lines, z_origin) origin = create_3D(lines, z_origin)
if invert : if invert :
extruded = create_3D(lines, z_origin - depth) extruded = create_3D(lines, z_origin - depth)
else: else: