- More projection and extrusion in place
This commit is contained in:
parent
b5c965bf2e
commit
cb471b4108
@ -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 = {} # 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]
|
||||
|
@ -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
53
main.py
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user