- Fixed 2d sketch with transfrom

This commit is contained in:
bklronin 2024-07-16 18:02:27 +02:00
parent 0c3e4eeb5e
commit c6f48a6e78
7 changed files with 273 additions and 200 deletions

21
doc/flow.md Normal file
View File

@ -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

3
doc/helper_commands.md Normal file
View File

@ -0,0 +1,3 @@
## Compile ui file
pyside6-uic gui.ui > Gui.py -g python

View File

@ -4,14 +4,10 @@ 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 from PySide6.QtGui import QPainter, QPen, QColor, QTransform
from PySide6.QtCore import Qt, QPoint, QPointF, Signal from PySide6.QtCore import Qt, QPoint, QPointF, Signal
from python_solvespace import SolverSystem, ResultFlag from python_solvespace import SolverSystem, ResultFlag
class DrawingTools():
pass
class Costrains():
pass
class SketchWidget(QWidget): class SketchWidget(QWidget):
constrain_done = Signal() constrain_done = Signal()
@ -59,13 +55,10 @@ class SketchWidget(QWidget):
"""Lines as orientation projected from the sketch""" """Lines as orientation projected from the sketch"""
for point in lines: for point in lines:
print(point)
x, y = point x, y = point
self.proj_snap_lines = lines self.proj_snap_lines = lines
self.proj_snap_points.append(QPoint(x, y)) coord = QPoint(x, y)
self.proj_snap_points.append(coord)
#point = self.solv.add_point_2d(x, y, self.wp)
"""relation_point = {} # Reinitialize the dictionary """relation_point = {} # Reinitialize the dictionary
#handle_nr = self.get_handle_nr(str(point)) #handle_nr = self.get_handle_nr(str(point))
@ -215,7 +208,6 @@ class SketchWidget(QWidget):
return None return None
def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint: def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
self.to_quadrant_coords(qt_pos)
return QPoint(self.to_quadrant_coords(qt_pos)) return QPoint(self.to_quadrant_coords(qt_pos))
def check_all_points(self,) -> list: def check_all_points(self,) -> list:
@ -647,11 +639,13 @@ class SketchWidget(QWidget):
painter.setPen(QPen(Qt.red, 4)) painter.setPen(QPen(Qt.red, 4))
painter.drawPoint(middle_x, middle_y) 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 # Set up the pen
pen = QPen(QColor('green')) # You can change the color as needed pen = QPen(QColor('green')) # You can change the color as needed
pen.setWidth(int(2 / self.zoom)) # Set the line widt)h pen.setWidth(int(2 / self.zoom)) # Set the line widt)h
painter.setPen(pen) painter.setPen(pen)
x = pos.x()
y = pos.y()
# Calculate the endpoints of the cross # Calculate the endpoints of the cross
half_size = size // 2 half_size = size // 2
@ -667,23 +661,47 @@ class SketchWidget(QWidget):
center_x = self.width() // 2 center_x = self.width() // 2
center_y = self.height() // 2 center_y = self.height() // 2
quadrant_x = point.x() - center_x 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 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): def paintEvent(self, event):
painter = QPainter(self) painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
self.drawAxes(painter) self.drawAxes(painter)
# Create a QTransform object
transform = QTransform()
# Translate the origin to the center of the widget # Translate the origin to the center of the widget
center = QPoint(self.width() // 2, self.height() // 2) center = QPointF(self.width() / 2, self.height() / 2)
painter.translate(center) transform.translate(center.x(), center.y())
# Apply the zoom factor # 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 = QPen(Qt.gray)
pen.setWidth(2 / self.zoom) pen.setWidthF(2 / self.zoom)
painter.setPen(pen) painter.setPen(pen)
# Draw points # Draw points
@ -700,36 +718,33 @@ class SketchWidget(QWidget):
painter.drawText(mid, str(round(dis, 2))) painter.drawText(mid, str(round(dis, 2)))
pen = QPen(Qt.green) pen = QPen(Qt.green)
pen.setWidth(2) pen.setWidthF(2 / self.zoom)
painter.setPen(pen) painter.setPen(pen)
if self.solv.entity_len(): if self.solv.entity_len():
for i in range(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) entity = self.solv.entity(i)
if entity.is_point_2d() and self.solv.params(entity.params): if entity.is_point_2d() and self.solv.params(entity.params):
x, y = 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) painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom)
#Highlight point hovered # Highlight point hovered
if self.hovered_point: if self.hovered_point:
highlight_pen = QPen(QColor(255, 0, 0)) highlight_pen = QPen(QColor(255, 0, 0))
highlight_pen.setWidth(2) highlight_pen.setWidthF(2 / self.zoom)
painter.setPen(highlight_pen) painter.setPen(highlight_pen)
painter.drawEllipse(self.hovered_point, 5 / self.zoom, 5 / self.zoom) painter.drawEllipse(self.hovered_point, 5 / self.zoom, 5 / self.zoom)
# Highlight line hovered # Highlight line hovered
if self.selected_line and not self.hovered_point: if self.selected_line and not self.hovered_point:
p1, p2 = self.selected_line p1, p2 = self.selected_line
painter.setPen(QPen(Qt.red, 2)) painter.setPen(QPen(Qt.red, 2 / self.zoom))
painter.drawLine(p1, p2) painter.drawLine(p1, p2)
for cross in self.proj_snap_lines: for cross in self.proj_snap_points:
# Calculate the endpoints of the cross self.draw_cross(painter, cross, 10 / self.zoom)
self.draw_cross(painter, cross[0], cross[1], 10)
# self.drawBackgroundGrid(painter)
painter.end() painter.end()
def wheelEvent(self, event): def wheelEvent(self, event):

View File

@ -15,6 +15,7 @@ class VTKWidget(QtWidgets.QWidget):
super().__init__(parent) super().__init__(parent)
self.access_selected_points = [] self.access_selected_points = []
self.selected_normal = None self.selected_normal = None
self.centroid = None
self.selected_edges = [] self.selected_edges = []
self.cell_normals = None self.cell_normals = None
@ -26,6 +27,7 @@ class VTKWidget(QtWidgets.QWidget):
self.picked_edge_actors = [] self.picked_edge_actors = []
self.displayed_normal_actors = [] self.displayed_normal_actors = []
self.body_actors_orig = []
self.flip_toggle = False self.flip_toggle = False
@ -102,6 +104,54 @@ class VTKWidget(QtWidgets.QWidget):
self.interactor.Initialize() self.interactor.Initialize()
self.interactor.Start() 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): def on_receive_command(self, command):
"""Calls the individual commands pressed in main""" """Calls the individual commands pressed in main"""
print("Receive command: ", command) print("Receive command: ", command)
@ -147,7 +197,7 @@ class VTKWidget(QtWidgets.QWidget):
# Create a transform for mirroring across the y-axis # Create a transform for mirroring across the y-axis
mirror_transform = vtk.vtkTransform() mirror_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)
@ -156,8 +206,7 @@ class VTKWidget(QtWidgets.QWidget):
mirror_transform.Scale(-1, -1, 1) # Inverting the original mirror look down mirror_transform.Scale(-1, -1, 1) # Inverting the original mirror look down
else: 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 # Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter() transformFilter = vtk.vtkTransformPolyDataFilter()
@ -172,7 +221,7 @@ class VTKWidget(QtWidgets.QWidget):
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetMapper(mapper) actor.SetMapper(mapper)
actor.GetProperty().SetColor(1.0, 1.0, 1.0) 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 # Add the actor to the scene
self.renderer.AddActor(actor) self.renderer.AddActor(actor)
@ -180,8 +229,9 @@ class VTKWidget(QtWidgets.QWidget):
mapper.Update() mapper.Update()
self.vtk_widget.GetRenderWindow().Render() 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): 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() points = vtk.vtkPoints()
# Use SetData with numpy array # Use SetData with numpy array
@ -209,32 +259,11 @@ class VTKWidget(QtWidgets.QWidget):
normalGenerator.ComputeCellNormalsOn() normalGenerator.ComputeCellNormalsOn()
normalGenerator.Update() 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()) self.cell_normals = vtk_to_numpy(normalGenerator.GetOutput().GetCellData().GetNormals())
# Create a mapper and actor # Create a mapper and actor
mapper = vtk.vtkPolyDataMapper() mapper = vtk.vtkPolyDataMapper()
mapper.SetInputData(transformFilter.GetOutput()) mapper.SetInputData(polydata)
actor = vtk.vtkActor() actor = vtk.vtkActor()
actor.SetMapper(mapper) actor.SetMapper(mapper)
@ -243,8 +272,13 @@ class VTKWidget(QtWidgets.QWidget):
actor.GetProperty().SetLineWidth(line_width) actor.GetProperty().SetLineWidth(line_width)
self.renderer.AddActor(actor) self.renderer.AddActor(actor)
self.body_actors_orig.append(actor)
self.vtk_widget.GetRenderWindow().Render() 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): def visualize_matrix(self, matrix):
points = vtk.vtkPoints() points = vtk.vtkPoints()
for i in range(4): for i in range(4):
@ -277,40 +311,6 @@ class VTKWidget(QtWidgets.QWidget):
return vtk_array 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: def get_points_and_edges_from_polydata(self, polydata) -> list:
# Extract points # Extract points
points = {} points = {}
@ -353,7 +353,7 @@ class VTKWidget(QtWidgets.QWidget):
center_of_mass.SetInputData(projected_mesh) center_of_mass.SetInputData(projected_mesh)
center_of_mass.SetUseScalarsAsWeights(False) center_of_mass.SetUseScalarsAsWeights(False)
center_of_mass.Update() center_of_mass.Update()
centroid = center_of_mass.GetCenter() centroid = np.array(center_of_mass.GetCenter())
# Create a coordinate system on the plane # Create a coordinate system on the plane
z_axis = np.array(normal) z_axis = np.array(normal)
@ -363,68 +363,33 @@ class VTKWidget(QtWidgets.QWidget):
x_axis = x_axis / np.linalg.norm(x_axis) x_axis = x_axis / np.linalg.norm(x_axis)
y_axis = np.cross(z_axis, x_axis) y_axis = np.cross(z_axis, x_axis)
# Create transformation matrix # Create rotation matrix (3x3)
matrix = vtk.vtkMatrix4x4() rotation_matrix = np.column_stack((x_axis, y_axis, z_axis))
for i in range(3):
matrix.SetElement(i, 0, x_axis[i]) # Store the full transformation for later use if needed
matrix.SetElement(i, 1, y_axis[i]) full_transform = np.eye(4)
matrix.SetElement(i, 2, z_axis[i]) full_transform[:3, :3] = rotation_matrix
matrix.SetElement(i, 3, centroid[i]) full_transform[:3, 3] = centroid
self.local_matrix = matrix self.local_matrix = full_transform
matrix.Invert()
# Transform points to 2D coordinates # Transform points to 2D coordinates
transform = vtk.vtkTransform() points = projected_mesh.GetPoints()
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()
xy_coordinates = [] xy_coordinates = []
for i in range(points.GetNumberOfPoints()): for i in range(points.GetNumberOfPoints()):
point = points.GetPoint(i) point = np.array(points.GetPoint(i))
xy_coordinates.append((-point[0], point[1]))
# 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 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)): def add_normal_line(self, origin, normal, length=10.0, color=(1, 0, 0)):
# Normalize the normal vector # Normalize the normal vector
normal = np.array(normal) normal = np.array(normal)
@ -520,7 +485,7 @@ class VTKWidget(QtWidgets.QWidget):
edge_actor = vtk.vtkActor() edge_actor = vtk.vtkActor()
edge_actor.SetMapper(edge_mapper) edge_actor.SetMapper(edge_mapper)
edge_actor.GetProperty().SetColor(1.0, 0.0, 0.0) # Red color for picked edges 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 # Add the actor to the renderer and store it
self.renderer.AddActor(edge_actor) self.renderer.AddActor(edge_actor)
@ -530,9 +495,18 @@ class VTKWidget(QtWidgets.QWidget):
self.compute_projection(False) self.compute_projection(False)
elif len(self.selected_edges) > 2: elif len(self.selected_edges) > 2:
del self.selected_edges[0]
pass 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 # Clear selection after projection was succesful
self.selected_edges = [] self.selected_edges = []
self.selected_normal = [] self.selected_normal = []
@ -559,16 +533,17 @@ class VTKWidget(QtWidgets.QWidget):
else: else:
self.selected_normal = selected_normal 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 # Draw the normal line
normal_length = 50 # Adjust this value to change the length of 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)) color=(1, 0, 0))
polydata = self.picker.GetActor().GetMapper().GetInput() 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() projected_points = projected_polydata.GetPoints()
#print("proj_points", projected_points) #print("proj_points", projected_points)

125
main.py
View File

@ -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 uuid
import names import names
from PySide6.QtCore import Qt, QPoint, Signal 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 # main, draw_widget, gl_widget
class ExtrudeDialog(QDialog): class ExtrudeDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -30,6 +36,7 @@ class ExtrudeDialog(QDialog):
# Symmetric checkbox # Symmetric checkbox
self.symmetric_checkbox = QCheckBox('Symmetric Extrude') self.symmetric_checkbox = QCheckBox('Symmetric Extrude')
self.invert_checkbox = QCheckBox('Invert Extrusion')
# OK and Cancel buttons # OK and Cancel buttons
button_layout = QHBoxLayout() button_layout = QHBoxLayout()
@ -43,12 +50,16 @@ class ExtrudeDialog(QDialog):
# Add all widgets to main layout # Add all widgets to main layout
layout.addLayout(length_layout) layout.addLayout(length_layout)
layout.addWidget(self.symmetric_checkbox) layout.addWidget(self.symmetric_checkbox)
layout.addLayout(length_layout)
layout.addWidget(self.invert_checkbox)
layout.addLayout(button_layout) layout.addLayout(button_layout)
self.setLayout(layout) self.setLayout(layout)
def get_values(self): 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): class MainWindow(QMainWindow):
send_command = Signal(str) send_command = Signal(str)
@ -129,7 +140,7 @@ class MainWindow(QMainWindow):
self.sketchWidget.create_proj_lines(edges) self.sketchWidget.create_proj_lines(edges)
# 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()
self.custom_3D_Widget.clear_actors_projection() self.custom_3D_Widget.clear_actors_projection()
#self.sketchWidget.create_workplane_space(edges, normal) #self.sketchWidget.create_workplane_space(edges, normal)
@ -189,13 +200,6 @@ class MainWindow(QMainWindow):
self.sketchWidget.mouse_mode = None self.sketchWidget.mouse_mode = None
self.sketchWidget.reset_buffers() 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): def draw_mesh(self):
name = self.ui.body_list.currentItem().text() name = self.ui.body_list.currentItem().text()
print("selected_for disp", name) print("selected_for disp", name)
@ -225,8 +229,10 @@ class MainWindow(QMainWindow):
points_for_interact = [] points_for_interact = []
for point_to_poly in self.sketchWidget.slv_lines_main: for point_to_poly in self.sketchWidget.slv_lines_main:
start, end = point_to_poly['ui_points'] start, end = point_to_poly['ui_points']
start_draw = self.translate_points_tup(start) from_coord_start = self.sketchWidget.from_quadrant_coords_no_center(start)
end_draw = self.translate_points_tup(end) 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 line = start_draw, end_draw
points_for_interact.append(line) points_for_interact.append(line)
@ -330,9 +336,44 @@ class MainWindow(QMainWindow):
if isinstance(point, QPoint): if isinstance(point, QPoint):
return point.x(), point.y() 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): def send_extrude(self):
is_symmetric = None is_symmetric = None
length = None length = None
invert = None
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']
@ -342,13 +383,10 @@ class MainWindow(QMainWindow):
#detect loop that causes problems in mesh generation #detect loop that causes problems in mesh generation
del points[-1] del points[-1]
"""length, ok = QInputDialog.getDouble(self, 'Extrude Length', 'Enter a mm value:', decimals=2)
#TODO : Implement cancel"""
dialog = ExtrudeDialog(self) dialog = ExtrudeDialog(self)
if dialog.exec(): if dialog.exec():
length, is_symmetric = dialog.get_values() length, is_symmetric, invert = dialog.get_values()
print(f"Extrude length: {length}, Symmetric: {is_symmetric}") print(f"Extrude length: {length}, Symmetric: {is_symmetric} Invert: {invert}")
else: else:
length = 0 length = 0
print("Extrude cancelled") print("Extrude cancelled")
@ -358,15 +396,31 @@ class MainWindow(QMainWindow):
# Rotation is done in vtk matrix trans # Rotation is done in vtk matrix trans
angle = 0 angle = 0
normal = self.custom_3D_Widget.selected_normal
print("Normie enter", normal)
if normal is None:
normal = [0, 0, 1] normal = [0, 0, 1]
f = geo.extrude_shape(points, length, angle, normal, is_symmetric)
if not is_symmetric: centroid = self.custom_3D_Widget.centroid
origin_z_lvl = length / 2 if centroid is None:
centroid = [0, 0, 0]
else: 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}" name_op = f"extrd-{name}"
element = { element = {
@ -388,7 +442,7 @@ class MainWindow(QMainWindow):
points = self.model['operation'][name]['sdf_object'] points = self.model['operation'][name]['sdf_object']
self.list_selected.append(points) self.list_selected.append(points)
if len(self.list_selected) > 1: if len(self.list_selected) == 2:
geo = Geometry() geo = Geometry()
f = geo.cut_shapes(self.list_selected[0], self.list_selected[1] ) f = geo.cut_shapes(self.list_selected[0], self.list_selected[1] )
@ -401,9 +455,12 @@ class MainWindow(QMainWindow):
name_op = f"cut-{name}" name_op = f"cut-{name}"
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)
items = self.ui.sketch_list.findItems(name_op, Qt.MatchExactly) items = self.ui.body_list.findItems(name_op, Qt.MatchExactly)
#self.ui.body_list.setCurrentItem(items[-1]) self.ui.body_list.setCurrentItem(items[-1])
self.custom_3D_Widget.clear_body_actors()
self.draw_mesh() self.draw_mesh()
elif len(self.list_selected) > 2:
self.list_selected.clear()
else: else:
print("mindestens 2!") print("mindestens 2!")
@ -443,16 +500,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, 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""" """2D to 3D sdf always first"""
f = polygon(points).rotate(angle) f = polygon(points)
f = f.extrude(length)
if not symet: # Calculate the offset vector (half the length in the direction of the normal)
print("Offsetting", symet) offset = [n * (length / 2) for n in normal]
f = f.extrude(length).orient(normal).translate((0, 0, length/2)) # orient(normal)
else: # Apply the offset in the direction of the normal
f = f.extrude(length).orient(normal) f = f.translate(offset)
# Apply the centroid translation
#f = f.translate(centroid)
# Apply the orientation
f = f.orient(normal)
return f return f

View File

@ -1,14 +1,12 @@
# 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, symmetric: bool = True): def generate_mesh(lines: list, z_origin: float, depth: float, invert :bool = False):
if symmetric:
depth1 = depth/2 origin = create_3D(lines, z_origin)
depth2 = -depth/2 if invert :
origin = create_3D(lines, z_origin, depth1) extruded = create_3D(lines, z_origin - depth)
extruded = create_3D(lines, z_origin, depth2)
else: 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) vert_lines = create_vert_lines(origin, extruded)
@ -26,16 +24,16 @@ def create_vert_lines(origin, extruded):
return vert_lines return vert_lines
def create_3D(lines, z_origin, depth): def create_3D(lines, z_pos):
line_loop = [] line_loop = []
for coordinate2d in lines: for coordinate2d in lines:
start, end = coordinate2d start, end = coordinate2d
xs, ys = start xs, ys = start
coordinate3d_start_orig = xs, -ys, z_origin + depth coordinate3d_start_orig = xs, -ys, z_pos
xe, ye = end 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 line3d_orig = coordinate3d_start_orig, coordinate3d_end_orig

View File

@ -1,7 +1,5 @@
from sdf import * from sdf import *
c = box(1).translate((0,0,0.2)) f = box(1).translate((1,1,-0.2))
f = capped_cylinder(-Z, Z, 0.5) c = hexagon(1).extrude(1).orient([0,0,-1])
c.orient([0.5, 0.5, 1]) c = f & c
c = f - c f.save("out.stl")
c.save("out.stl")