From df336aaea75e20b3cbaa239b1154f979a650aa83 Mon Sep 17 00:00:00 2001 From: bklronin Date: Thu, 18 Jul 2024 20:42:01 +0200 Subject: [PATCH] Layered rendering inverted interactor Befrore fork --- drawing_modules/draw_widget2d.py | 3 +- drawing_modules/vtk_widget.py | 105 ++++++++++++++++++++----------- main.py | 82 +++++++++++++++++++++--- 3 files changed, 144 insertions(+), 46 deletions(-) diff --git a/drawing_modules/draw_widget2d.py b/drawing_modules/draw_widget2d.py index 4ef7b86..b00ef32 100644 --- a/drawing_modules/draw_widget2d.py +++ b/drawing_modules/draw_widget2d.py @@ -780,10 +780,11 @@ class SketchWidget(QWidget): def clear_sketch(self): self.slv_points_main = [] self.slv_lines_main = [] + self.proj_snap_lines.clear() + self.proj_snap_points.clear() self.reset_buffers() self.solv = SolverSystem() - # Example usage if __name__ == "__main__": import sys diff --git a/drawing_modules/vtk_widget.py b/drawing_modules/vtk_widget.py index 9836d3f..658afa7 100644 --- a/drawing_modules/vtk_widget.py +++ b/drawing_modules/vtk_widget.py @@ -30,6 +30,7 @@ class VTKWidget(QtWidgets.QWidget): self.picked_edge_actors = [] self.displayed_normal_actors = [] self.body_actors_orig = [] + self.projected_mesh_actors = [] self.flip_toggle = False @@ -40,14 +41,34 @@ class VTKWidget(QtWidgets.QWidget): # Create VTK pipeline self.renderer = vtk.vtkRenderer() - self.vtk_widget.GetRenderWindow().AddRenderer(self.renderer) - self.interactor = self.vtk_widget.GetRenderWindow().GetInteractor() + self.renderer_projections = vtk.vtkRenderer() + self.renderer_indicators = vtk.vtkRenderer() - # Set up the camera - self.camera = self.renderer.GetActiveCamera() + self.renderer.SetViewport(0, 0, 1, 1) # Full viewport + self.renderer_projections.SetViewport(0, 0, 1, 1) # Full viewport, overlays the first + self.renderer_indicators.SetViewport(0, 0, 1, 1) # Full viewport, overlays the first + + self.renderer.SetLayer(0) + self.renderer_projections.SetLayer(1) + self.renderer_indicators.SetLayer(2) # This will be on top + + # Add renderers to the render window + render_window = self.vtk_widget.GetRenderWindow() + render_window.SetNumberOfLayers(3) + render_window.AddRenderer(self.renderer) + render_window.AddRenderer(self.renderer_projections) + render_window.AddRenderer(self.renderer_indicators) + + # Set up shared camera + self.camera = vtk.vtkCamera() self.camera.SetPosition(5, 5, 1000) self.camera.SetFocalPoint(0, 0, 0) - self.camera.SetClippingRange(0.1, 10000) + self.camera.SetClippingRange(0.1, 100000) + self.renderer.SetActiveCamera(self.camera) + self.renderer_projections.SetActiveCamera(self.camera) + self.renderer_indicators.SetActiveCamera(self.camera) + + self.interactor = self.vtk_widget.GetRenderWindow().GetInteractor() # Light Setup def add_light(renderer, position, color=(1, 1, 1), intensity=1.0): @@ -65,7 +86,6 @@ class VTKWidget(QtWidgets.QWidget): add_light(self.renderer, (0, 0, 1000), intensity=1.5) add_light(self.renderer, (0, 0, -1000), intensity=1.5) - # Set up picking self.picker = vtk.vtkCellPicker() self.picker.SetTolerance(0.005) @@ -120,6 +140,13 @@ class VTKWidget(QtWidgets.QWidget): self.renderer.AddActor(actor) + def update_render(self): + self.renderer.ResetCameraClippingRange() + self.renderer_projections.ResetCameraClippingRange() + self.renderer_indicators.ResetCameraClippingRange() + self.vtk_widget.GetRenderWindow().Render() + + def create_grid(self, size=100, spacing=10): # Create a vtkPoints object and store the points in it points = vtk.vtkPoints() @@ -398,10 +425,10 @@ class VTKWidget(QtWidgets.QWidget): 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") + def compute_2d_coordinates_line(self, line_source, normal): + # Ensure the input is a vtkLineSource + if not isinstance(line_source, vtk.vtkLineSource): + raise ValueError("Input must be a vtkLineSource") # Normalize the normal vector normal = np.array(normal) @@ -422,19 +449,9 @@ class VTKWidget(QtWidgets.QWidget): 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) + # Get the polydata from the line source + line_source.Update() + polydata = line_source.GetOutput() # Apply the transform to the polydata transform_filter = vtk.vtkTransformPolyDataFilter() @@ -543,6 +560,7 @@ class VTKWidget(QtWidgets.QWidget): def on_invert_normal(self): # Kippstufe für Normal flip if self.selected_normal is not None: + self.clear_actors_normals() self.compute_projection(self.flip_toggle) def on_click(self, obj, event): @@ -563,7 +581,6 @@ class VTKWidget(QtWidgets.QWidget): # Ensure it's a line if cell.GetCellType() == vtk.VTK_LINE: - self.selected_vtk_line.append(cell) # Get the two points of the line point_id1 = cell.GetPointId(0) @@ -588,6 +605,8 @@ class VTKWidget(QtWidgets.QWidget): line_source.SetPoint1(point1) line_source.SetPoint2(point2) + self.selected_vtk_line.append(line_source) + # Create a mapper and actor for the picked edge edge_mapper = vtk.vtkPolyDataMapper() edge_mapper.SetInputConnection(line_source.GetOutputPort()) @@ -595,21 +614,27 @@ class VTKWidget(QtWidgets.QWidget): edge_actor = vtk.vtkActor() edge_actor.SetMapper(edge_mapper) edge_actor.GetProperty().SetColor(1.0, 0.0, 0.0) # Red color for picked edges - edge_actor.GetProperty().SetLineWidth(8) # Make the line thicker + edge_actor.GetProperty().SetLineWidth(5) # Make the line thicker # Add the actor to the renderer and store it - self.renderer.AddActor(edge_actor) + self.renderer_indicators.AddActor(edge_actor) self.picked_edge_actors.append(edge_actor) if len(self.selected_edges) == 2: self.compute_projection(False) if len(self.selected_edges) > 2: + # Clear lists for selection self.selected_vtk_line.clear() self.selected_edges.clear() - self.clear_actors_projection() self.clear_edge_select() + # Clear Actors from view + self.clear_actors_projection() + self.clear_actors_sel_edges() + self.clear_actors_normals() + + def find_origin_vertex(self, edge1, edge2): if edge1[0] == edge2[0]or edge1[0] == edge2[1]: return edge1[0] @@ -625,13 +650,18 @@ class VTKWidget(QtWidgets.QWidget): def clear_actors_projection(self): """Removes all actors that were used for projection""" - for edge_line in self.picked_edge_actors: - self.renderer.RemoveActor(edge_line) + for flat_mesh in self.projected_mesh_actors: + self.renderer_projections.RemoveActor(flat_mesh) + def clear_actors_normals(self): for normals in self.displayed_normal_actors: - self.renderer.RemoveActor(normals) + self.renderer_indicators.RemoveActor(normals) - def compute_projection(self, direction_invert: bool= False): + def clear_actors_sel_edges(self): + for edge_line in self.picked_edge_actors: + self.renderer_indicators.RemoveActor(edge_line) + + def compute_projection(self, direction_invert: bool = False): # Compute the normal from the two selected edges ) edge1 = self.selected_edges[0][1] - self.selected_edges[0][0] @@ -662,28 +692,31 @@ class VTKWidget(QtWidgets.QWidget): self.project_tosketch_points = self.compute_2d_coordinates(projected_polydata, self.selected_normal) # Seperately rotate selected edges for drawing + self.project_tosketch_lines.clear() 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) + print("outgoing lines", self.project_tosketch_lines) # Create a mapper and actor for the projected data mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(projected_polydata) + # Projected mesh in green actor = vtk.vtkActor() actor.SetMapper(mapper) #actor.GetProperty().SetRenderLinesAsTubes(True) actor.GetProperty().SetColor(0.0, 1.0, 0.0) # Set color to green - actor.GetProperty().SetLineWidth(8) # Set line width + actor.GetProperty().SetLineWidth(4) # Set line width - self.renderer.AddActor(normal_actor) + self.renderer_indicators.AddActor(normal_actor) self.displayed_normal_actors.append(normal_actor) - # Add the edge lines actor to the scene - self.renderer.AddActor(actor) - self.picked_edge_actors.append(actor) + self.renderer_projections.AddActor(actor) + self.projected_mesh_actors.append(actor) # Render the scene + self.update_render() self.vtk_widget.GetRenderWindow().Render() def start(self): diff --git a/main.py b/main.py index aa0163d..c069699 100644 --- a/main.py +++ b/main.py @@ -14,6 +14,7 @@ from drawing_modules.draw_widget2d import SketchWidget from sdf import * from python_solvespace import SolverSystem, ResultFlag from mesh_modules import simple_mesh, vesta_mesh, interactor_mesh +from dataclasses import dataclass, field # main, draw_widget, gl_widget @@ -22,6 +23,11 @@ class ExtrudeDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle('Extrude Options') + def create_hline(): + line = QLabel() + line.setStyleSheet("border-top: 1px solid #cccccc;") # Light grey line + line.setFixedHeight(1) + return line layout = QVBoxLayout() @@ -37,6 +43,11 @@ class ExtrudeDialog(QDialog): # Symmetric checkbox self.symmetric_checkbox = QCheckBox('Symmetric Extrude') self.invert_checkbox = QCheckBox('Invert Extrusion') + self.cut_checkbox = QCheckBox('Perform Cut') + self.union_checkbox = QCheckBox('Combine') + self.rounded_checkbox = QCheckBox('Round Edges') + self.seperator = create_hline() + # OK and Cancel buttons button_layout = QHBoxLayout() @@ -49,17 +60,21 @@ class ExtrudeDialog(QDialog): # Add all widgets to main layout layout.addLayout(length_layout) + layout.addWidget(self.seperator) + layout.addWidget(self.cut_checkbox) + layout.addWidget(self.union_checkbox) + layout.addWidget(self.seperator) layout.addWidget(self.symmetric_checkbox) - - layout.addLayout(length_layout) layout.addWidget(self.invert_checkbox) + layout.addWidget(self.seperator) + layout.addWidget(self.rounded_checkbox) layout.addLayout(button_layout) self.setLayout(layout) def get_values(self): - return self.length_input.value(), self.symmetric_checkbox.isChecked() ,self.invert_checkbox.isChecked() + return self.length_input.value(), self.symmetric_checkbox.isChecked() ,self.invert_checkbox.isChecked(), self.cut_checkbox.isChecked(), self.union_checkbox.isChecked(), self.rounded_checkbox.isChecked() class MainWindow(QMainWindow): send_command = Signal(str) @@ -143,8 +158,11 @@ class MainWindow(QMainWindow): self.sketchWidget.create_proj_lines(selected_lines) # CLear all selections after it has been projected - #self.custom_3D_Widget.clear_edge_select() + self.custom_3D_Widget.project_tosketch_points.clear() + self.custom_3D_Widget.project_tosketch_lines.clear() self.custom_3D_Widget.clear_actors_projection() + self.custom_3D_Widget.clear_actors_normals() + #self.custom_3D_Widget.clear_actors_projection() #self.sketchWidget.create_workplane_space(edges, normal) @@ -387,7 +405,7 @@ class MainWindow(QMainWindow): dialog = ExtrudeDialog(self) if dialog.exec(): - length, is_symmetric, invert = dialog.get_values() + length, is_symmetric, invert, cut, union_with, rounded = dialog.get_values() print(f"Extrude length: {length}, Symmetric: {is_symmetric} Invert: {invert}") else: length = 0 @@ -396,7 +414,7 @@ class MainWindow(QMainWindow): geo = Geometry() normal = self.custom_3D_Widget.selected_normal - print("Normie enter", normal) + #print("Normie enter", normal) if normal is None: normal = [0, 0, 1] @@ -405,7 +423,7 @@ class MainWindow(QMainWindow): centroid = [0, 0, 0] else: centroid = list(centroid) - print("This centroid ", centroid) + #print("This centroid ", centroid) f = geo.extrude_shape(points, length, normal, centroid, is_symmetric, invert, 0) @@ -419,7 +437,10 @@ class MainWindow(QMainWindow): ### Interactor lines = self.convert_lines_for_interactor() - edges = interactor_mesh.generate_mesh(lines, 0, length) + if not invert: + edges = interactor_mesh.generate_mesh(lines, 0, length) + else: + edges = interactor_mesh.generate_mesh(lines, 0, -length) offset_vector = geo.vector_to_centroid(None, centroid, normal) #print("off_ved", offset_vector) @@ -466,8 +487,8 @@ class MainWindow(QMainWindow): self.custom_3D_Widget.load_stl(file) self.custom_3D_Widget.update() -class Geometry: +class Geometry: def angle_between_normals(self, normal1, normal2): # Ensure the vectors are normalized n1 = normal1 / np.linalg.norm(normal1) @@ -586,6 +607,49 @@ class Geometry: except Exception as e: print("Error executing code:", e) + +@dataclass +class Component: + """The base container combinging all related elements""" + id = None + sketch = None + interactor = None + body = None + +@dataclass +class Sketch: + """All of the 2D Information of a sketch""" + origin = None + plane = None + normal = None + ui_points = None + ui_lines = None + slv_points = None + proj_points = None + proj_lines = None + +@dataclass +class Interactor: + """Helper mesh consisting of edges for selection""" + edges = None + faces = None + +@dataclass +class Body: + """The actual body as sdf3 object""" + sketch = None + height = None + sdf_body = None + +class Boolean: + """Toolkit that contains the boolean operations for sdf3 objects""" + def extrude(self, sketch: Sketch): + pass + +class Helper: + pass + + if __name__ == "__main__": app = QApplication([]) window = MainWindow()