- More projection and extrusion in place

This commit is contained in:
bklronin 2024-07-10 23:19:43 +02:00
parent b5c965bf2e
commit cb471b4108
4 changed files with 185 additions and 90 deletions

View File

@ -22,6 +22,8 @@ class SketchWidget(QWidget):
self.drag_buffer = [None, None]
self.main_buffer = [None, None]
self.proj_snap_points = []
self.hovered_point = None
self.selected_line = None
@ -54,23 +56,20 @@ class SketchWidget(QWidget):
def create_proj_lines(self, lines):
"""Lines as orientation projected from the sketch"""
for line in lines:
for point in line:
x, y, z = point
for point in lines:
print(point)
x, y = point
self.proj_snap_points = lines
point = self.solv.add_point_2d(x, y, self.wp)
#point = self.solv.add_point_2d(x, y, self.wp)
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)
"""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)
print("points", self.slv_points_main)
print("lines", self.slv_lines_main)
print("lines", lines)
self.slv_points_main.append(relation_point)"""
def find_duplicate_points_2d(self, edges):
points = []
@ -635,6 +634,21 @@ class SketchWidget(QWidget):
painter.setPen(QPen(Qt.red, 4))
painter.drawPoint(middle_x, middle_y)
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
painter.setPen(pen)
# Calculate the endpoints of the cross
half_size = size // 2
# Draw the horizontal line
painter.drawLine(x - half_size, y, x + half_size, y)
# Draw the vertical line
painter.drawLine(x, y - half_size, x, y + half_size)
def to_quadrant_coords(self, point):
"""Translate linear coordinates to quadrant coordinates."""
center_x = self.width() // 2
@ -663,6 +677,10 @@ 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]

View File

@ -61,6 +61,24 @@ class VTKWidget(QtWidgets.QWidget):
# Add observer for mouse clicks
self.interactor.AddObserver("RightButtonPressEvent", self.on_click)
# Add axis gizmo (smaller size)
self.axes = vtk.vtkAxesActor()
self.axes.SetTotalLength(0.5, 0.5, 0.5) # Reduced size
self.axes.SetShaftType(0)
self.axes.SetAxisLabels(1)
# Create an orientation marker
self.axes_widget = vtk.vtkOrientationMarkerWidget()
self.axes_widget.SetOrientationMarker(self.axes)
self.axes_widget.SetInteractor(self.interactor)
self.axes_widget.SetViewport(0.0, 0.0, 0.2, 0.2) # Set position and size
self.axes_widget.EnabledOn()
self.axes_widget.InteractiveOff()
# Start the interactor
self.interactor.Initialize()
self.interactor.Start()
@staticmethod
def compute_normal_from_lines(line1, line2):
vec1 = line1[1] - line1[0]
@ -138,11 +156,23 @@ 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
mirror_transform = vtk.vtkTransform()
mirror_transform.Scale(-1, -1, 1) # This mirrors across the x-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(polydata)
mapper.SetInputData(transformFilter.GetOutput())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
@ -212,6 +242,67 @@ class VTKWidget(QtWidgets.QWidget):
return points, edges
def project_mesh_to_plane(self, input_mesh, normal, origin):
# Create the projector
projector = vtk.vtkProjectPointsToPlane()
projector.SetInputData(input_mesh)
projector.SetProjectionTypeToSpecifiedPlane()
# Set the normal and origin of the plane
projector.SetNormal(normal)
projector.SetOrigin(origin)
# Execute the projection
projector.Update()
# Get the projected mesh
projected_mesh = projector.GetOutput()
return projected_mesh
def compute_2d_coordinates(self, projected_mesh, normal):
# Compute centroid of projected points
center_of_mass = vtk.vtkCenterOfMass()
center_of_mass.SetInputData(projected_mesh)
center_of_mass.SetUseScalarsAsWeights(False)
center_of_mass.Update()
centroid = center_of_mass.GetCenter()
# Create a coordinate system on the plane
z_axis = np.array(normal)
x_axis = np.cross(z_axis, [0, 0, 1])
if np.allclose(x_axis, 0):
x_axis = np.cross(z_axis, [0, 1, 0])
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])
matrix.Invert()
# Transform points to 2D coordinates
transform = vtk.vtkTransform()
transform.SetMatrix(matrix)
transformer = vtk.vtkTransformPolyDataFilter()
transformer.SetInputData(projected_mesh)
transformer.SetTransform(transform)
transformer.Update()
transformed_mesh = transformer.GetOutput()
points = transformed_mesh.GetPoints()
xy_coordinates = []
for i in range(points.GetNumberOfPoints()):
point = points.GetPoint(i)
xy_coordinates.append((point[0], point[1]))
return xy_coordinates
def on_click(self, obj, event):
click_pos = self.interactor.GetEventPosition()
@ -274,43 +365,15 @@ class VTKWidget(QtWidgets.QWidget):
self.selected_normal = self.selected_normal / np.linalg.norm(self.selected_normal)
print("Computed normal:", self.selected_normal)
# Compute the centroid of the selected edges
centroid = (
0, 0, 0) # point1 #np.mean([point for edge in self.selected_edges for point in edge], axis=0)
centroid = np.mean([point for edge in self.selected_edges for point in edge], axis=0)
# Create a transform for projection
transform = vtk.vtkTransform()
transform.Identity()
projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, centroid)
projected_points = projected_polydata.GetPoints()
print("proj_points", projected_points)
# Translate to center the transform on the centroid
transform.Translate(-centroid[0], -centroid[1], -centroid[2])
# Compute rotation to align normal with Z-axis
z_axis = np.array([0, 0, 1])
rotation_axis = np.cross(self.selected_normal, z_axis)
rotation_angle = np.arccos(np.dot(self.selected_normal, z_axis)) * 180 / np.pi
if np.linalg.norm(rotation_axis) > 1e-6: # Avoid division by zero
transform.RotateWXYZ(rotation_angle, rotation_axis[0], rotation_axis[1], rotation_axis[2])
# Apply scaling to flatten
transform.Scale(1, 1, 0)
# Apply the inverse rotation to bring it back to original orientation
transform.RotateWXYZ(-rotation_angle, rotation_axis[0], rotation_axis[1], rotation_axis[2])
# Translate back
transform.Translate(centroid[0], centroid[1], centroid[2])
# Apply the transform to the polydata
transformFilter = vtk.vtkTransformPolyDataFilter()
transformFilter.SetInputData(polydata)
transformFilter.SetTransform(transform)
transformFilter.Update()
# Get the projected polydata
projected_polydata = transformFilter.GetOutput()
print("Polydata", projected_polydata)
# Extract 2D coordinates
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
mapper = vtk.vtkPolyDataMapper()
@ -324,41 +387,14 @@ class VTKWidget(QtWidgets.QWidget):
# Add the actor to the scene
self.renderer.AddActor(actor)
# Add a plane to visualize the projection plane
plane_source = vtk.vtkPlaneSource()
plane_source.SetCenter(centroid)
plane_source.SetNormal(self.selected_normal)
plane_mapper = vtk.vtkPolyDataMapper()
plane_mapper.SetInputConnection(plane_source.GetOutputPort())
plane_actor = vtk.vtkActor()
plane_actor.SetMapper(plane_mapper)
plane_actor.GetProperty().SetColor(0.8, 0.8, 0.8) # Light gray
plane_actor.GetProperty().SetOpacity(0.5)
self.renderer.AddActor(plane_actor)
# Reset camera to show all actors
self.renderer.ResetCamera()
# Render and interact
self.vtk_widget.GetRenderWindow().Render()
self.project_tosketch_edge = self.get_points_and_edges_from_polydata(projected_polydata)
print("Edges", self.project_tosketch_edge[0])
elif len(self.access_selected_points) > 2:
self.access_selected_points = []
# Clear the picked edge actors
for actor in self.picked_edge_actors:
self.renderer.RemoveActor(actor)
self.picked_edge_actors.clear()
# Reset selected edges
if len(self.selected_edges) > 2:
self.selected_edges = []
else:
print("Selected cell is not a line")
self.selected_normal = []
# Render the scene
self.vtk_widget.GetRenderWindow().Render()
def start(self):
self.interactor.Initialize()
self.interactor.Start()

53
main.py
View File

@ -295,7 +295,20 @@ class MainWindow(QMainWindow):
#Create and draw Interactor
geo = Geometry()
f = geo.extrude_shape(points, length)
# 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]
angle = 0
f = geo.extrude_shape(points, length, angle, normal)
name_op = f"extrd-{name}"
element = {
@ -316,7 +329,6 @@ class MainWindow(QMainWindow):
name = self.ui.body_list.currentItem().text()
points = self.model['operation'][name]['sdf_object']
self.list_selected.append(points)
#print(self.list_selected)
if len(self.list_selected) > 1:
geo = Geometry()
@ -332,8 +344,8 @@ class MainWindow(QMainWindow):
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])
#self.draw_mesh()
#self.ui.body_list.setCurrentItem(items[-1])
self.draw_mesh()
else:
print("mindestens 2!")
@ -342,17 +354,46 @@ class MainWindow(QMainWindow):
self.custom_3D_Widget.update()
class Geometry:
def angle_between_normals(self, normal1, normal2):
# Ensure the vectors are normalized
n1 = normal1 / np.linalg.norm(normal1)
n2 = normal2 / np.linalg.norm(normal2)
# Compute the dot product
dot_product = np.dot(n1, n2)
# Clip the dot product to the valid range [-1, 1]
dot_product = np.clip(dot_product, -1.0, 1.0)
# Compute the angle in radians
angle_rad = np.arccos(dot_product)
# Convert to degrees if needed
angle_deg = np.degrees(angle_rad)
print("Angle deg", angle_deg)
return angle_rad
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):
def extrude_shape(self, points, length: float, angle, normal):
"""2D to 3D sdf always first"""
f = polygon(points).extrude(length)
f = polygon(points).rotate(angle)
f = f.extrude(length).orient(normal) # orient(normal)
return f
def mirror_body(self, sdf_object3d):
f = sdf_object3d.rotate(pi)
return f
def cut_shapes(self, sdf_object1, sdf_object2):
f = difference(sdf_object1, sdf_object2) # equivalent
return f

View File

@ -31,11 +31,11 @@ def create_3D(lines, z_origin, depth):
for coordinate2d in lines:
start, end = coordinate2d
xs,ys = start
coordinate3d_start_orig = xs, ys, z_origin + depth
xs, ys = start
coordinate3d_start_orig = xs, -ys, z_origin + depth
xe, ye = end
coordinate3d_end_orig = xe, ye, z_origin + depth
coordinate3d_end_orig = xe, -ye, z_origin + depth
line3d_orig = coordinate3d_start_orig, coordinate3d_end_orig