- Working project and draw on exiting

This commit is contained in:
bklronin 2024-07-11 20:16:20 +02:00
parent cb471b4108
commit d2b8d9540a
4 changed files with 256 additions and 55 deletions

View File

@ -637,7 +637,7 @@ class SketchWidget(QWidget):
def draw_cross(self, painter, x, y, size=10):
# Set up the pen
pen = QPen(QColor('red')) # You can change the color as needed
pen.setWidth(2) # Set the line width
pen.setWidth(int(2 / self.zoom)) # Set the line widt)h
painter.setPen(pen)
# Calculate the endpoints of the cross
@ -677,10 +677,6 @@ class SketchWidget(QWidget):
for point in self.slv_points_main:
painter.drawEllipse(point['ui_point'], 3 / self.zoom, 3 / self.zoom)
for cross in self.proj_snap_points:
# Calculate the endpoints of the cross
self.draw_cross(painter, cross[0], cross[1], 10)
for dic in self.slv_lines_main:
p1 = dic['ui_points'][0]
p2 = dic['ui_points'][1]
@ -716,6 +712,10 @@ class SketchWidget(QWidget):
painter.setPen(QPen(Qt.red, 2))
painter.drawLine(p1, p2)
for cross in self.proj_snap_points:
# Calculate the endpoints of the cross
self.draw_cross(painter, cross[0], cross[1], 10)
# self.drawBackgroundGrid(painter)
painter.end()

View File

@ -18,6 +18,8 @@ class VTKWidget(QtWidgets.QWidget):
self.selected_edges = []
self.cell_normals = None
self.local_matrix = None
self.project_tosketch_edge = []
self.vtk_widget = QVTKRenderWindowInteractor(self)
@ -113,10 +115,30 @@ class VTKWidget(QtWidgets.QWidget):
polydata.SetPoints(points)
polydata.SetLines(lines)
# Create a transform for mirroring across the y-axis
mirror_transform = vtk.vtkTransform()
if self.local_matrix:
print(self.local_matrix)
matrix = vtk.vtkMatrix4x4()
matrix.DeepCopy(self.local_matrix)
matrix.Invert()
mirror_transform.SetMatrix(matrix)
mirror_transform.Scale(-1, -1, 1) # Inverting the original mirror look down
else:
pass
#mirror_transform.Scale(1, -1, 1) # This mirrors across the y-axis
# Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputData(polydata)
transformFilter.SetTransform(mirror_transform)
transformFilter.Update()
# Create a mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polydata)
mapper.SetInputData(transformFilter.GetOutput())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
@ -130,18 +152,21 @@ class VTKWidget(QtWidgets.QWidget):
mapper.Update()
self.vtk_widget.GetRenderWindow().Render()
def render_from_points_direct_with_faces(self, vertices, faces):
def render_from_points_direct_with_faces(self, vertices, faces, color=(1, 1, 1), line_width=2, point_size=5):
points = vtk.vtkPoints()
for i in range(vertices.shape[0]):
points.InsertNextPoint(vertices[i])
# Use SetData with numpy array
vtk_array = numpy_to_vtk(vertices, deep=True)
points.SetData(vtk_array)
# Create a vtkCellArray to store the triangles
triangles = vtk.vtkCellArray()
for i in range(faces.shape[0]):
for face in faces:
triangle = vtk.vtkTriangle()
triangle.GetPointIds().SetId(0, faces[i, 0])
triangle.GetPointIds().SetId(1, faces[i, 1])
triangle.GetPointIds().SetId(2, faces[i, 2])
triangle.GetPointIds().SetId(0, face[0])
triangle.GetPointIds().SetId(1, face[1])
triangle.GetPointIds().SetId(2, face[2])
triangles.InsertNextCell(triangle)
# Create a polydata object
@ -156,11 +181,19 @@ class VTKWidget(QtWidgets.QWidget):
normalGenerator.ComputeCellNormalsOn()
normalGenerator.Update()
### There might be aproblem earlier but this fixes the drawing for now.
#TODO: Investigate upstream conversion errors.
# Create a transform for mirroring across the x-axis
# Create a transform for mirroring across the y-axis
mirror_transform = vtk.vtkTransform()
mirror_transform.Scale(-1, -1, 1) # This mirrors across the x-axis
if self.local_matrix:
print(self.local_matrix)
matrix = vtk.vtkMatrix4x4()
matrix.DeepCopy(self.local_matrix)
matrix.Invert()
mirror_transform.SetMatrix(matrix)
mirror_transform.Scale(-1, 1, 1) #Inverting the original mirror look down
else:
mirror_transform.Scale(1, -1, 1) # This mirrors across the y-axis
# Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter()
@ -176,19 +209,45 @@ class VTKWidget(QtWidgets.QWidget):
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
actor.GetProperty().SetColor(color)
actor.GetProperty().EdgeVisibilityOn()
actor.GetProperty().SetLineWidth(line_width)
# (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
# Force an update of the pipeline
#mapper.Update()
self.renderer.AddActor(actor)
self.vtk_widget.GetRenderWindow().Render()
def visualize_matrix(self, matrix):
points = vtk.vtkPoints()
for i in range(4):
for j in range(4):
points.InsertNextPoint(matrix.GetElement(0, j),
matrix.GetElement(1, j),
matrix.GetElement(2, j))
polydata = vtk.vtkPolyData()
polydata.SetPoints(points)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polydata)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetPointSize(5)
self.renderer.AddActor(actor)
def numpy_to_vtk(self, array, deep=True):
"""Convert a numpy array to a vtk array."""
vtk_array = vtk.vtkDoubleArray()
vtk_array.SetNumberOfComponents(array.shape[1])
vtk_array.SetNumberOfTuples(array.shape[0])
for i in range(array.shape[0]):
for j in range(array.shape[1]):
vtk_array.SetComponent(i, j, array[i, j])
return vtk_array
def load_custom_mesh(self, vertices, faces):
### Load meshes by own module
# Create a vtkPoints object and store the points in it
@ -282,11 +341,13 @@ class VTKWidget(QtWidgets.QWidget):
matrix.SetElement(i, 1, y_axis[i])
matrix.SetElement(i, 2, z_axis[i])
matrix.SetElement(i, 3, centroid[i])
self.local_matrix = matrix
matrix.Invert()
# Transform points to 2D coordinates
transform = vtk.vtkTransform()
transform.SetMatrix(matrix)
transform.Scale([1,1,1])
transformer = vtk.vtkTransformPolyDataFilter()
transformer.SetInputData(projected_mesh)
@ -299,10 +360,84 @@ class VTKWidget(QtWidgets.QWidget):
xy_coordinates = []
for i in range(points.GetNumberOfPoints()):
point = points.GetPoint(i)
xy_coordinates.append((point[0], point[1]))
xy_coordinates.append((-point[0], point[1]))
return xy_coordinates
def create_normal_gizmo(self, normal, scale=1.0):
# Normalize the normal vector
normal = np.array(normal)
normal = normal / np.linalg.norm(normal)
# Create an arrow source
arrow_source = vtk.vtkArrowSource()
arrow_source.SetTipResolution(20)
arrow_source.SetShaftResolution(20)
# Create a transform to orient and position the arrow
transform = vtk.vtkTransform()
# Translate to the origin point
transform.SetMatrix(self.local_matrix)
# Apply the transform to the arrow
transform_filter = vtk.vtkTransformPolyDataFilter()
transform_filter.SetInputConnection(arrow_source.GetOutputPort())
transform_filter.SetTransform(transform)
transform_filter.Update()
# Create mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(transform_filter.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(1, 0, 0) # Red color for the arrow
return actor
def add_normal_line(self, origin, normal, length=10.0, color=(1, 0, 0)):
# Normalize the normal vector
normal = np.array(normal)
normal = normal / np.linalg.norm(normal)
# Calculate the end point
end_point = origin + normal * length
# Create vtkPoints
points = vtk.vtkPoints()
points.InsertNextPoint(origin)
points.InsertNextPoint(end_point)
# Create a line
line = vtk.vtkLine()
line.GetPointIds().SetId(0, 0)
line.GetPointIds().SetId(1, 1)
# Create a cell array to store the line
lines = vtk.vtkCellArray()
lines.InsertNextCell(line)
# Create a polydata to store everything in
polyData = vtk.vtkPolyData()
polyData.SetPoints(points)
polyData.SetLines(lines)
# Create mapper and actor
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(polyData)
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(color)
actor.GetProperty().SetLineWidth(2) # Adjust line width as needed
# Add to renderer
self.renderer.AddActor(actor)
self.vtk_widget.GetRenderWindow().Render()
return actor # Return the actor in case you need to remove or modify it later
def on_click(self, obj, event):
click_pos = self.interactor.GetEventPosition()
@ -367,6 +502,11 @@ class VTKWidget(QtWidgets.QWidget):
centroid = np.mean([point for edge in self.selected_edges for point in edge], axis=0)
# Draw the normal line
normal_length = 50 # Adjust this value to change the length of the normal line
normal_actor = self.add_normal_line(centroid, self.selected_normal, length=normal_length,
color=(1, 0, 0))
projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, centroid)
projected_points = projected_polydata.GetPoints()
print("proj_points", projected_points)
@ -384,12 +524,20 @@ class VTKWidget(QtWidgets.QWidget):
actor.GetProperty().SetColor(0.0, 1.0, 0.0) # Set color to green
actor.GetProperty().SetLineWidth(4) # Set line width
self.renderer.AddActor(normal_actor)
# Add the actor to the scene
self.renderer.AddActor(actor)
if len(self.selected_edges) > 2:
# Clear selection after
self.selected_edges = []
self.selected_normal = []
for edge_line in self.picked_edge_actors:
self.renderer.RemoveActor(edge_line)
elif len(self.selected_edges) > 2:
pass
# Render the scene

94
main.py
View File

@ -1,7 +1,7 @@
import uuid
import names
from PySide6.QtCore import Qt, QPoint
from PySide6.QtWidgets import QApplication, QMainWindow, QSizePolicy, QInputDialog
from PySide6.QtWidgets import QApplication, QMainWindow, QSizePolicy, QInputDialog, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox, QCheckBox, QPushButton
from Gui import Ui_fluencyCAD # Import the generated GUI module
from drawing_modules.vtk_widget import VTKWidget
from drawing_modules.vysta_widget import PyVistaWidget
@ -13,6 +13,44 @@ from mesh_modules import simple_mesh, vesta_mesh, interactor_mesh
# main, draw_widget, gl_widget
class ExtrudeDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle('Extrude Options')
layout = QVBoxLayout()
# Length input
length_layout = QHBoxLayout()
length_label = QLabel('Extrude Length (mm):')
self.length_input = QDoubleSpinBox()
self.length_input.setDecimals(2)
self.length_input.setRange(0, 1000) # Adjust range as needed
length_layout.addWidget(length_label)
length_layout.addWidget(self.length_input)
# Symmetric checkbox
self.symmetric_checkbox = QCheckBox('Symmetric Extrude')
# OK and Cancel buttons
button_layout = QHBoxLayout()
ok_button = QPushButton('OK')
cancel_button = QPushButton('Cancel')
ok_button.clicked.connect(self.accept)
cancel_button.clicked.connect(self.reject)
button_layout.addWidget(ok_button)
button_layout.addWidget(cancel_button)
# Add all widgets to main layout
layout.addLayout(length_layout)
layout.addWidget(self.symmetric_checkbox)
layout.addLayout(button_layout)
self.setLayout(layout)
def get_values(self):
return self.length_input.value(), self.symmetric_checkbox.isChecked()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
@ -184,7 +222,6 @@ class MainWindow(QMainWindow):
return points_for_interact
def add_sketch(self):
name = f"sketch-{str(names.get_first_name())}"
points_for_sdf = self.convert_points_for_sdf()
@ -281,6 +318,8 @@ class MainWindow(QMainWindow):
return point.x(), point.y()
def send_extrude(self):
is_symmetric = None
length = None
selected = self.ui.sketch_list.currentItem()
name = selected.text()
points = self.model['sketch'][name]['sketch_points']
@ -290,25 +329,31 @@ class MainWindow(QMainWindow):
#detect loop that causes problems in mesh generation
del points[-1]
length, ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
#TODO : Implement cancel
"""length, ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
#TODO : Implement cancel"""
dialog = ExtrudeDialog(self)
if dialog.exec():
length, is_symmetric = dialog.get_values()
print(f"Extrude length: {length}, Symmetric: {is_symmetric}")
else:
length = 0
print("Extrude cancelled")
#Create and draw Interactor
geo = Geometry()
# Get the nromal form the 3d widget for extrusion
if self.custom_3D_Widget.selected_normal is not None:
normal = self.custom_3D_Widget.selected_normal.tolist()
print("normal", normal)
angle = geo.angle_between_normals([0, 1, 0], normal)
print("Angle", angle)
f = geo.extrude_shape(points, length, angle, normal)
f = geo.mirror_body(f)
else:
normal = [0,0,-1]
# Rotation is done in vtk matrix trans
angle = 0
f = geo.extrude_shape(points, length, angle, normal)
normal = [0, 0, 1]
f = geo.extrude_shape(points, length, angle, normal, is_symmetric)
if not is_symmetric:
origin_z_lvl = length / 2
else:
origin_z_lvl = 0
self.calc_sketch_projection_3d(lines, origin_z_lvl, length)
name_op = f"extrd-{name}"
element = {
@ -322,7 +367,7 @@ class MainWindow(QMainWindow):
self.ui.body_list.addItem(name_op)
items = self.ui.body_list.findItems(name_op, Qt.MatchExactly)[0]
self.ui.body_list.setCurrentItem(items)
self.calc_sketch_projection_3d(lines, 0, length)
self.draw_mesh()
def send_cut(self):
@ -375,16 +420,26 @@ class Geometry:
return angle_rad
def offset_syn(self, f, length):
f = f.translate((0,0, length / 2))
return f
def distance(self, p1, p2):
"""Calculate the distance between two points."""
print("p1", p1)
print("p2", p2)
return math.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
def extrude_shape(self, points, length: float, angle, normal):
def extrude_shape(self, points, length: float, angle, normal, symet: bool = True):
"""2D to 3D sdf always first"""
f = polygon(points).rotate(angle)
f = f.extrude(length).orient(normal) # orient(normal)
if not symet:
print("Offsetting", symet)
f = f.extrude(length).orient(normal).translate((0, 0, length/2)) # orient(normal)
else:
f = f.extrude(length).orient(normal)
return f
@ -393,7 +448,6 @@ class Geometry:
return f
def cut_shapes(self, sdf_object1, sdf_object2):
f = difference(sdf_object1, sdf_object2) # equivalent
return f

View File

@ -1,8 +1,7 @@
from sdf import *
c = box(1).translate((0,0,0.2))
f = capped_cylinder(-Z, Z, 0.5)
c.orient([0.5, 0.5, 1])
c = f - c
f = sphere(1) & box(0.9)
c = cylinder(0.3)
f -= c.orient(X) | c.orient(Y) | c.orient(Z)
stl_object = None
f.save("out.stl", step=0.05)
c.save("out.stl")