diff --git a/doc/flow.md b/doc/flow.md new file mode 100644 index 0000000..9d8e3d1 --- /dev/null +++ b/doc/flow.md @@ -0,0 +1,21 @@ +# Signal Flow +## 2D SketchWidget + +- 2D QPoint form custom Qpainter widget in linear space +- 2D QPoint ot cartesian space +- 2D tuple into slvspace dict system and solvespace +- get calced position from Solvespace solver +- add to internal reference dict +- Transform to linear QPainter space for display to show + +## 3D custom Widget + +- Take Tuple points form solvespace main dict +- Draw Interactor and sdfCAD model + +### Select and Project + +- Project cartesian flattened mesh into 2D +- Transform to 2D xy +- Transform to linear space for 2D widget to draw. +- Result into 2D cartesian for body interaction extrude etc \ No newline at end of file diff --git a/doc/helper_commands.md b/doc/helper_commands.md new file mode 100644 index 0000000..c95e6ad --- /dev/null +++ b/doc/helper_commands.md @@ -0,0 +1,3 @@ +## Compile ui file +pyside6-uic gui.ui > Gui.py -g python + diff --git a/drawing_modules/draw_widget2d.py b/drawing_modules/draw_widget2d.py index 2dfa326..ce6fe34 100644 --- a/drawing_modules/draw_widget2d.py +++ b/drawing_modules/draw_widget2d.py @@ -4,14 +4,10 @@ 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.QtGui import QPainter, QPen, QColor, QTransform from PySide6.QtCore import Qt, QPoint, QPointF, Signal from python_solvespace import SolverSystem, ResultFlag -class DrawingTools(): - pass -class Costrains(): - pass class SketchWidget(QWidget): constrain_done = Signal() @@ -59,13 +55,10 @@ class SketchWidget(QWidget): """Lines as orientation projected from the sketch""" for point in lines: - print(point) x, y = point self.proj_snap_lines = lines - self.proj_snap_points.append(QPoint(x, y)) - - - #point = self.solv.add_point_2d(x, y, self.wp) + coord = QPoint(x, y) + self.proj_snap_points.append(coord) """relation_point = {} # Reinitialize the dictionary #handle_nr = self.get_handle_nr(str(point)) @@ -215,7 +208,6 @@ class SketchWidget(QWidget): return None def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint: - self.to_quadrant_coords(qt_pos) return QPoint(self.to_quadrant_coords(qt_pos)) def check_all_points(self,) -> list: @@ -647,11 +639,13 @@ class SketchWidget(QWidget): painter.setPen(QPen(Qt.red, 4)) painter.drawPoint(middle_x, middle_y) - def draw_cross(self, painter, x, y, size=10): + def draw_cross(self, painter, pos: QPoint, size=10): # Set up the pen pen = QPen(QColor('green')) # You can change the color as needed pen.setWidth(int(2 / self.zoom)) # Set the line widt)h painter.setPen(pen) + x = pos.x() + y = pos.y() # Calculate the endpoints of the cross half_size = size // 2 @@ -667,23 +661,47 @@ class SketchWidget(QWidget): center_x = self.width() // 2 center_y = self.height() // 2 quadrant_x = point.x() - center_x - quadrant_y = point.y() - center_y + quadrant_y = center_y - point.y() # Note the change here return QPoint(quadrant_x, quadrant_y) / self.zoom + def from_quadrant_coords(self, point: QPoint): + """Translate quadrant coordinates to linear coordinates.""" + center_x = self.width() // 2 + center_y = self.height() // 2 + widget_x = center_x + point.x() * self.zoom + widget_y = center_y - point.y() * self.zoom # Note the subtraction here + + return QPoint(int(widget_x), int(widget_y)) + + def from_quadrant_coords_no_center(self, point): + """Translate quadrant coordinates to linear coordinates.""" + center_x = 0 + center_y = 0 + widget_x = center_x + point.x() * self.zoom + widget_y = center_y - point.y() * self.zoom # Note the subtraction here + return QPoint(int(widget_x), int(widget_y)) + def paintEvent(self, event): painter = QPainter(self) + painter.setRenderHint(QPainter.Antialiasing) self.drawAxes(painter) + # Create a QTransform object + transform = QTransform() + # Translate the origin to the center of the widget - center = QPoint(self.width() // 2, self.height() // 2) - painter.translate(center) + center = QPointF(self.width() / 2, self.height() / 2) + transform.translate(center.x(), center.y()) # Apply the zoom factor - painter.scale(self.zoom, self.zoom) + transform.scale(self.zoom, -self.zoom) # Negative y-scale to invert y-axis + + # Set the transform to the painter + painter.setTransform(transform) pen = QPen(Qt.gray) - pen.setWidth(2 / self.zoom) + pen.setWidthF(2 / self.zoom) painter.setPen(pen) # Draw points @@ -700,36 +718,33 @@ class SketchWidget(QWidget): painter.drawText(mid, str(round(dis, 2))) pen = QPen(Qt.green) - pen.setWidth(2) + pen.setWidthF(2 / self.zoom) painter.setPen(pen) if self.solv.entity_len(): for i in range(self.solv.entity_len()): - # 3 Entitys in the beginning of the workplane normal and point entity = self.solv.entity(i) if entity.is_point_2d() and self.solv.params(entity.params): x, y = self.solv.params(entity.params) - point = QPoint(x, y) + point = QPointF(x, y) painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom) - #Highlight point hovered + # Highlight point hovered if self.hovered_point: highlight_pen = QPen(QColor(255, 0, 0)) - highlight_pen.setWidth(2) + highlight_pen.setWidthF(2 / self.zoom) painter.setPen(highlight_pen) painter.drawEllipse(self.hovered_point, 5 / self.zoom, 5 / self.zoom) # Highlight line hovered if self.selected_line and not self.hovered_point: p1, p2 = self.selected_line - painter.setPen(QPen(Qt.red, 2)) + painter.setPen(QPen(Qt.red, 2 / self.zoom)) painter.drawLine(p1, p2) - for cross in self.proj_snap_lines: - # Calculate the endpoints of the cross - self.draw_cross(painter, cross[0], cross[1], 10) + for cross in self.proj_snap_points: + self.draw_cross(painter, cross, 10 / self.zoom) - # self.drawBackgroundGrid(painter) painter.end() def wheelEvent(self, event): diff --git a/drawing_modules/vtk_widget.py b/drawing_modules/vtk_widget.py index 0646cf5..4482439 100644 --- a/drawing_modules/vtk_widget.py +++ b/drawing_modules/vtk_widget.py @@ -15,6 +15,7 @@ class VTKWidget(QtWidgets.QWidget): super().__init__(parent) self.access_selected_points = [] self.selected_normal = None + self.centroid = None self.selected_edges = [] self.cell_normals = None @@ -26,6 +27,7 @@ class VTKWidget(QtWidgets.QWidget): self.picked_edge_actors = [] self.displayed_normal_actors = [] + self.body_actors_orig = [] self.flip_toggle = False @@ -102,6 +104,54 @@ class VTKWidget(QtWidgets.QWidget): self.interactor.Initialize() self.interactor.Start() + # Create the grid + grid = self.create_grid(size=100, spacing=10) + + # Setup actor and mapper + mapper = vtk.vtkPolyDataMapper() + mapper.SetInputData(grid) + + actor = vtk.vtkActor() + actor.SetMapper(mapper) + actor.GetProperty().SetColor(0.5, 0.5, 0.5) # Set grid color to gray + + self.renderer.AddActor(actor) + def create_grid(self, size=100, spacing=10): + # Create a vtkPoints object and store the points in it + points = vtk.vtkPoints() + + # Create lines + lines = vtk.vtkCellArray() + + # Create the grid + for i in range(-size, size + 1, spacing): + # X-direction line + points.InsertNextPoint(i, -size, 0) + points.InsertNextPoint(i, size, 0) + line = vtk.vtkLine() + line.GetPointIds().SetId(0, points.GetNumberOfPoints() - 2) + line.GetPointIds().SetId(1, points.GetNumberOfPoints() - 1) + lines.InsertNextCell(line) + + # Y-direction line + points.InsertNextPoint(-size, i, 0) + points.InsertNextPoint(size, i, 0) + line = vtk.vtkLine() + line.GetPointIds().SetId(0, points.GetNumberOfPoints() - 2) + line.GetPointIds().SetId(1, points.GetNumberOfPoints() - 1) + lines.InsertNextCell(line) + + # Create a polydata to store everything in + grid = vtk.vtkPolyData() + + # Add the points to the dataset + grid.SetPoints(points) + + # Add the lines to the dataset + grid.SetLines(lines) + + return grid + def on_receive_command(self, command): """Calls the individual commands pressed in main""" print("Receive command: ", command) @@ -147,7 +197,7 @@ class VTKWidget(QtWidgets.QWidget): # Create a transform for mirroring across the y-axis mirror_transform = vtk.vtkTransform() - if self.local_matrix: + """if self.local_matrix: print(self.local_matrix) matrix = vtk.vtkMatrix4x4() matrix.DeepCopy(self.local_matrix) @@ -156,8 +206,7 @@ class VTKWidget(QtWidgets.QWidget): 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 + mirror_transform.Scale(1, 1, 1) # This mirrors across the y-axis""" # Apply the transform to the polydata transformFilter = vtk.vtkTransformPolyDataFilter() @@ -172,7 +221,7 @@ class VTKWidget(QtWidgets.QWidget): actor = vtk.vtkActor() actor.SetMapper(mapper) actor.GetProperty().SetColor(1.0, 1.0, 1.0) - actor.GetProperty().SetLineWidth(2) # Set line width + actor.GetProperty().SetLineWidth(4) # Set line width # Add the actor to the scene self.renderer.AddActor(actor) @@ -180,8 +229,9 @@ class VTKWidget(QtWidgets.QWidget): mapper.Update() self.vtk_widget.GetRenderWindow().Render() - def render_from_points_direct_with_faces(self, vertices, faces, color=(0.1, 0.2, 0.8), line_width=2, point_size=5): + """Sketch Widget has inverted Y axiis therefore we invert y via scale here until fix""" + points = vtk.vtkPoints() # Use SetData with numpy array @@ -209,32 +259,11 @@ class VTKWidget(QtWidgets.QWidget): normalGenerator.ComputeCellNormalsOn() normalGenerator.Update() - # Create a transform for mirroring across the y-axis - mirror_transform = vtk.vtkTransform() - - if self.local_matrix: - """Transforming to the position of the sketch projection with invert 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() - transformFilter.SetInputData(polydata) - transformFilter.SetTransform(mirror_transform) - transformFilter.Update() - self.cell_normals = vtk_to_numpy(normalGenerator.GetOutput().GetCellData().GetNormals()) # Create a mapper and actor mapper = vtk.vtkPolyDataMapper() - mapper.SetInputData(transformFilter.GetOutput()) + mapper.SetInputData(polydata) actor = vtk.vtkActor() actor.SetMapper(mapper) @@ -243,8 +272,13 @@ class VTKWidget(QtWidgets.QWidget): actor.GetProperty().SetLineWidth(line_width) self.renderer.AddActor(actor) + self.body_actors_orig.append(actor) self.vtk_widget.GetRenderWindow().Render() + def clear_body_actors(self): + for actor in self.body_actors_orig: + self.renderer.RemoveActor(actor) + def visualize_matrix(self, matrix): points = vtk.vtkPoints() for i in range(4): @@ -277,40 +311,6 @@ class VTKWidget(QtWidgets.QWidget): 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 - points = vtk.vtkPoints() - for vertex in vertices: - points.InsertNextPoint(vertex) - - # Create a vtkCellArray to store the faces - cells = vtk.vtkCellArray() - for face in faces: - triangle = vtk.vtkTriangle() - triangle.GetPointIds().SetId(0, face[0]) - triangle.GetPointIds().SetId(1, face[1]) - triangle.GetPointIds().SetId(2, face[2]) - cells.InsertNextCell(triangle) - - # Create a polydata object - polydata = vtk.vtkPolyData() - polydata.SetPoints(points) - polydata.SetPolys(cells) - - # Create mapper and actor - mapper = vtk.vtkPolyDataMapper() - mapper.SetInputData(polydata) # Make sure this line is present - actor = vtk.vtkActor() - actor.SetMapper(mapper) - - # Add to renderer - self.renderer.AddActor(actor) - - # Force an update of the pipeline - mapper.Update() - self.vtk_widget.GetRenderWindow().Render() - def get_points_and_edges_from_polydata(self, polydata) -> list: # Extract points points = {} @@ -353,7 +353,7 @@ class VTKWidget(QtWidgets.QWidget): center_of_mass.SetInputData(projected_mesh) center_of_mass.SetUseScalarsAsWeights(False) center_of_mass.Update() - centroid = center_of_mass.GetCenter() + centroid = np.array(center_of_mass.GetCenter()) # Create a coordinate system on the plane z_axis = np.array(normal) @@ -363,68 +363,33 @@ class VTKWidget(QtWidgets.QWidget): x_axis = x_axis / np.linalg.norm(x_axis) y_axis = np.cross(z_axis, x_axis) - # Create transformation matrix - matrix = vtk.vtkMatrix4x4() - for i in range(3): - matrix.SetElement(i, 0, x_axis[i]) - 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() + # Create rotation matrix (3x3) + rotation_matrix = np.column_stack((x_axis, y_axis, z_axis)) + + # Store the full transformation for later use if needed + full_transform = np.eye(4) + full_transform[:3, :3] = rotation_matrix + full_transform[:3, 3] = centroid + self.local_matrix = full_transform # Transform points to 2D coordinates - transform = vtk.vtkTransform() - transform.SetMatrix(matrix) - transform.Scale([1,1,1]) - - transformer = vtk.vtkTransformPolyDataFilter() - transformer.SetInputData(projected_mesh) - transformer.SetTransform(transform) - transformer.Update() - - transformed_mesh = transformer.GetOutput() - points = transformed_mesh.GetPoints() - + points = projected_mesh.GetPoints() xy_coordinates = [] + for i in range(points.GetNumberOfPoints()): - point = points.GetPoint(i) - xy_coordinates.append((-point[0], point[1])) + point = np.array(points.GetPoint(i)) + + # Translate point to origin + point_centered = point - centroid + + # Rotate point + rotated_point = np.dot(rotation_matrix.T, point_centered) + + # Store only x and y coordinates + xy_coordinates.append((rotated_point[0], rotated_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) @@ -520,7 +485,7 @@ 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(4) # Make the line thicker + edge_actor.GetProperty().SetLineWidth(8) # Make the line thicker # Add the actor to the renderer and store it self.renderer.AddActor(edge_actor) @@ -530,9 +495,18 @@ class VTKWidget(QtWidgets.QWidget): self.compute_projection(False) elif len(self.selected_edges) > 2: + del self.selected_edges[0] pass - def clear_edge_select(self, ): + def find_origin_vertex(self, edge1, edge2): + if edge1[0] == edge2[0]or edge1[0] == edge2[1]: + return edge1[0] + elif edge1[1] == edge2[0] or edge1[1] == edge2[1]: + return edge1[1] + else: + return None # The edges don't share a vertex + + def clear_edge_select(self ): # Clear selection after projection was succesful self.selected_edges = [] self.selected_normal = [] @@ -559,21 +533,22 @@ class VTKWidget(QtWidgets.QWidget): else: self.selected_normal = selected_normal - centroid = np.mean([point for edge in self.selected_edges for point in edge], axis=0) + self.centroid = np.mean([point for edge in self.selected_edges for point in edge], axis=0) + #self.centroid = self.find_origin_vertex(edge1, edge2) # 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, + normal_actor = self.add_normal_line(self.centroid, self.selected_normal, length=normal_length, color=(1, 0, 0)) polydata = self.picker.GetActor().GetMapper().GetInput() - projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, 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 - self.project_tosketch_edge = self.compute_2d_coordinates(projected_polydata, self.selected_normal) + self.project_tosketch_edge = self.compute_2d_coordinates(projected_polydata, self.selected_normal) #print("3d_points_proj", self.project_tosketch_edge) # Create a mapper and actor for the projected data diff --git a/main.py b/main.py index c39de80..a538690 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,8 @@ +# nuitka-project: --plugin-enable=pyside6 +# nuitka-project: --plugin-enable=numpy +# nuitka-project: --standalone +# nuitka-project: --macos-create-app-bundle + import uuid import names from PySide6.QtCore import Qt, QPoint, Signal @@ -12,6 +17,7 @@ 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) @@ -30,6 +36,7 @@ class ExtrudeDialog(QDialog): # Symmetric checkbox self.symmetric_checkbox = QCheckBox('Symmetric Extrude') + self.invert_checkbox = QCheckBox('Invert Extrusion') # OK and Cancel buttons button_layout = QHBoxLayout() @@ -43,12 +50,16 @@ class ExtrudeDialog(QDialog): # Add all widgets to main layout layout.addLayout(length_layout) layout.addWidget(self.symmetric_checkbox) + + layout.addLayout(length_layout) + layout.addWidget(self.invert_checkbox) + layout.addLayout(button_layout) self.setLayout(layout) def get_values(self): - return self.length_input.value(), self.symmetric_checkbox.isChecked() + return self.length_input.value(), self.symmetric_checkbox.isChecked() ,self.invert_checkbox.isChecked() class MainWindow(QMainWindow): send_command = Signal(str) @@ -129,7 +140,7 @@ class MainWindow(QMainWindow): self.sketchWidget.create_proj_lines(edges) # CLear all selections after it has been projected - self.custom_3D_Widget.clear_edge_select() + #self.custom_3D_Widget.clear_edge_select() self.custom_3D_Widget.clear_actors_projection() #self.sketchWidget.create_workplane_space(edges, normal) @@ -189,13 +200,6 @@ class MainWindow(QMainWindow): self.sketchWidget.mouse_mode = None self.sketchWidget.reset_buffers() - def calc_sketch_projection_3d(self, lines, z_origin, depth): - print(f"Gemetries {lines}, {z_origin}, {depth}") - - edges = interactor_mesh.generate_mesh(lines, z_origin, depth) - print("final_mesh", edges) - self.custom_3D_Widget.load_interactor_mesh(edges) - def draw_mesh(self): name = self.ui.body_list.currentItem().text() print("selected_for disp", name) @@ -225,8 +229,10 @@ class MainWindow(QMainWindow): points_for_interact = [] for point_to_poly in self.sketchWidget.slv_lines_main: start, end = point_to_poly['ui_points'] - start_draw = self.translate_points_tup(start) - end_draw = self.translate_points_tup(end) + from_coord_start = self.sketchWidget.from_quadrant_coords_no_center(start) + from_coord_end = self.sketchWidget.from_quadrant_coords_no_center(end) + start_draw = self.translate_points_tup(from_coord_start) + end_draw = self.translate_points_tup(from_coord_end) line = start_draw, end_draw points_for_interact.append(line) @@ -330,9 +336,44 @@ class MainWindow(QMainWindow): if isinstance(point, QPoint): return point.x(), point.y() + def rotate_point_to_normal(self, centroid, normal): + # Ensure the normal is a unit vector + normal = normal / np.linalg.norm(normal) + + # Initial direction (assuming positive Z-axis) + initial_direction = np.array([0, 0, 1]) + + # Compute rotation axis + rotation_axis = np.cross(initial_direction, normal) + + # If rotation axis is zero (vectors are parallel), no rotation is needed + if np.allclose(rotation_axis, 0): + return centroid + + # Compute rotation angle + rotation_angle = np.arccos(np.dot(initial_direction, normal)) + + # Create rotation matrix using Rodrigues' formula + K = np.array([ + [0, -rotation_axis[2], rotation_axis[1]], + [rotation_axis[2], 0, -rotation_axis[0]], + [-rotation_axis[1], rotation_axis[0], 0] + ]) + rotation_matrix = ( + np.eye(3) + + np.sin(rotation_angle) * K + + (1 - np.cos(rotation_angle)) * np.dot(K, K) + ) + + # Apply rotation to centroid + rotated_centroid = np.dot(rotation_matrix, centroid) + + return rotated_centroid + def send_extrude(self): is_symmetric = None length = None + invert = None selected = self.ui.sketch_list.currentItem() name = selected.text() points = self.model['sketch'][name]['sketch_points'] @@ -342,13 +383,10 @@ 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""" - dialog = ExtrudeDialog(self) if dialog.exec(): - length, is_symmetric = dialog.get_values() - print(f"Extrude length: {length}, Symmetric: {is_symmetric}") + length, is_symmetric, invert = dialog.get_values() + print(f"Extrude length: {length}, Symmetric: {is_symmetric} Invert: {invert}") else: length = 0 print("Extrude cancelled") @@ -358,15 +396,31 @@ class MainWindow(QMainWindow): # Rotation is done in vtk matrix trans angle = 0 - normal = [0, 0, 1] - f = geo.extrude_shape(points, length, angle, normal, is_symmetric) - if not is_symmetric: - origin_z_lvl = length / 2 + normal = self.custom_3D_Widget.selected_normal + print("Normie enter", normal) + if normal is None: + normal = [0, 0, 1] + + centroid = self.custom_3D_Widget.centroid + if centroid is None: + centroid = [0, 0, 0] else: - origin_z_lvl = 0 + centroid = list(centroid) + print("THis centroid ",centroid) - self.calc_sketch_projection_3d(lines, origin_z_lvl, length) + f = geo.extrude_shape(points, length, angle, normal, centroid, is_symmetric, invert) + + 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}" element = { @@ -388,7 +442,7 @@ class MainWindow(QMainWindow): points = self.model['operation'][name]['sdf_object'] self.list_selected.append(points) - if len(self.list_selected) > 1: + if len(self.list_selected) == 2: geo = Geometry() f = geo.cut_shapes(self.list_selected[0], self.list_selected[1] ) @@ -401,9 +455,12 @@ class MainWindow(QMainWindow): name_op = f"cut-{name}" self.model['operation'][name_op] = element self.ui.body_list.addItem(name_op) - items = self.ui.sketch_list.findItems(name_op, Qt.MatchExactly) - #self.ui.body_list.setCurrentItem(items[-1]) + items = self.ui.body_list.findItems(name_op, Qt.MatchExactly) + self.ui.body_list.setCurrentItem(items[-1]) + self.custom_3D_Widget.clear_body_actors() self.draw_mesh() + elif len(self.list_selected) > 2: + self.list_selected.clear() else: print("mindestens 2!") @@ -443,16 +500,22 @@ class Geometry: 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, symet: bool = True): + def extrude_shape(self, points, length: float, angle, normal, centroid, symet: bool = True, invert: bool = False): """2D to 3D sdf always first""" - f = polygon(points).rotate(angle) + f = polygon(points) + f = f.extrude(length) - if not symet: - print("Offsetting", symet) - f = f.extrude(length).orient(normal).translate((0, 0, length/2)) # orient(normal) + # Calculate the offset vector (half the length in the direction of the normal) + offset = [n * (length / 2) for n in normal] - else: - f = f.extrude(length).orient(normal) + # Apply the offset in the direction of the normal + f = f.translate(offset) + + # Apply the centroid translation + #f = f.translate(centroid) + + # Apply the orientation + f = f.orient(normal) return f diff --git a/mesh_modules/interactor_mesh.py b/mesh_modules/interactor_mesh.py index 916ddc3..e8d2b3a 100644 --- a/mesh_modules/interactor_mesh.py +++ b/mesh_modules/interactor_mesh.py @@ -1,20 +1,18 @@ # Draw simple boundary based on the lines and depth -def generate_mesh(lines: list, z_origin: float, depth: float, symmetric: bool = True): - if symmetric: - depth1 = depth/2 - depth2 = -depth/2 - origin = create_3D(lines, z_origin, depth1) - extruded = create_3D(lines, z_origin, depth2) +def generate_mesh(lines: list, z_origin: float, depth: float, invert :bool = False): + + origin = create_3D(lines, z_origin) + if invert : + extruded = create_3D(lines, z_origin - depth) else: - origin = create_3D(lines, z_origin, 0) - extruded = create_3D(lines, z_origin, depth) + extruded = create_3D(lines, z_origin + depth) vert_lines = create_vert_lines(origin, extruded) print(f"Result = {origin} / {extruded} / {vert_lines}") - return origin + vert_lines + extruded + return origin + vert_lines + extruded def create_vert_lines(origin, extruded): @@ -26,16 +24,16 @@ def create_vert_lines(origin, extruded): return vert_lines -def create_3D(lines, z_origin, depth): +def create_3D(lines, z_pos): line_loop = [] for coordinate2d in lines: start, end = coordinate2d xs, ys = start - coordinate3d_start_orig = xs, -ys, z_origin + depth + coordinate3d_start_orig = xs, -ys, z_pos xe, ye = end - coordinate3d_end_orig = xe, -ye, z_origin + depth + coordinate3d_end_orig = xe, -ye, z_pos line3d_orig = coordinate3d_start_orig, coordinate3d_end_orig diff --git a/meshtest.py b/meshtest.py index 1b976cf..eb1eaea 100644 --- a/meshtest.py +++ b/meshtest.py @@ -1,7 +1,5 @@ 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 - -c.save("out.stl") \ No newline at end of file +f = box(1).translate((1,1,-0.2)) +c = hexagon(1).extrude(1).orient([0,0,-1]) +c = f & c +f.save("out.stl") \ No newline at end of file