- 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.drag_buffer = [None, None]
|
||||||
self.main_buffer = [None, None]
|
self.main_buffer = [None, None]
|
||||||
|
|
||||||
|
self.proj_snap_points = []
|
||||||
|
|
||||||
self.hovered_point = None
|
self.hovered_point = None
|
||||||
self.selected_line = None
|
self.selected_line = None
|
||||||
|
|
||||||
@ -54,23 +56,20 @@ class SketchWidget(QWidget):
|
|||||||
def create_proj_lines(self, lines):
|
def create_proj_lines(self, lines):
|
||||||
"""Lines as orientation projected from the sketch"""
|
"""Lines as orientation projected from the sketch"""
|
||||||
|
|
||||||
for line in lines:
|
for point in lines:
|
||||||
for point in line:
|
print(point)
|
||||||
x, y, z = 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
|
"""relation_point = {} # Reinitialize the dictionary
|
||||||
handle_nr = self.get_handle_nr(str(point))
|
#handle_nr = self.get_handle_nr(str(point))
|
||||||
relation_point['handle_nr'] = handle_nr
|
#relation_point['handle_nr'] = handle_nr
|
||||||
relation_point['solv_handle'] = point
|
#relation_point['solv_handle'] = point
|
||||||
relation_point['ui_point'] = QPoint(x, y)
|
relation_point['ui_point'] = QPoint(x, y)
|
||||||
|
|
||||||
self.slv_points_main.append(relation_point)
|
self.slv_points_main.append(relation_point)"""
|
||||||
|
|
||||||
print("points", self.slv_points_main)
|
|
||||||
print("lines", self.slv_lines_main)
|
|
||||||
print("lines", lines)
|
|
||||||
|
|
||||||
def find_duplicate_points_2d(self, edges):
|
def find_duplicate_points_2d(self, edges):
|
||||||
points = []
|
points = []
|
||||||
@ -635,6 +634,21 @@ 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):
|
||||||
|
# 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):
|
def to_quadrant_coords(self, point):
|
||||||
"""Translate linear coordinates to quadrant coordinates."""
|
"""Translate linear coordinates to quadrant coordinates."""
|
||||||
center_x = self.width() // 2
|
center_x = self.width() // 2
|
||||||
@ -663,6 +677,10 @@ class SketchWidget(QWidget):
|
|||||||
for point in self.slv_points_main:
|
for point in self.slv_points_main:
|
||||||
painter.drawEllipse(point['ui_point'], 3 / self.zoom, 3 / self.zoom)
|
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:
|
for dic in self.slv_lines_main:
|
||||||
p1 = dic['ui_points'][0]
|
p1 = dic['ui_points'][0]
|
||||||
p2 = dic['ui_points'][1]
|
p2 = dic['ui_points'][1]
|
||||||
|
@ -61,6 +61,24 @@ class VTKWidget(QtWidgets.QWidget):
|
|||||||
# Add observer for mouse clicks
|
# Add observer for mouse clicks
|
||||||
self.interactor.AddObserver("RightButtonPressEvent", self.on_click)
|
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
|
@staticmethod
|
||||||
def compute_normal_from_lines(line1, line2):
|
def compute_normal_from_lines(line1, line2):
|
||||||
vec1 = line1[1] - line1[0]
|
vec1 = line1[1] - line1[0]
|
||||||
@ -138,11 +156,23 @@ class VTKWidget(QtWidgets.QWidget):
|
|||||||
normalGenerator.ComputeCellNormalsOn()
|
normalGenerator.ComputeCellNormalsOn()
|
||||||
normalGenerator.Update()
|
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())
|
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(polydata)
|
mapper.SetInputData(transformFilter.GetOutput())
|
||||||
|
|
||||||
actor = vtk.vtkActor()
|
actor = vtk.vtkActor()
|
||||||
actor.SetMapper(mapper)
|
actor.SetMapper(mapper)
|
||||||
@ -212,6 +242,67 @@ class VTKWidget(QtWidgets.QWidget):
|
|||||||
|
|
||||||
return points, edges
|
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):
|
def on_click(self, obj, event):
|
||||||
click_pos = self.interactor.GetEventPosition()
|
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)
|
self.selected_normal = self.selected_normal / np.linalg.norm(self.selected_normal)
|
||||||
print("Computed normal:", self.selected_normal)
|
print("Computed normal:", self.selected_normal)
|
||||||
|
|
||||||
# Compute the centroid of the selected edges
|
centroid = np.mean([point for edge in self.selected_edges for point in edge], axis=0)
|
||||||
centroid = (
|
|
||||||
0, 0, 0) # point1 #np.mean([point for edge in self.selected_edges for point in edge], axis=0)
|
|
||||||
|
|
||||||
# Create a transform for projection
|
projected_polydata = self.project_mesh_to_plane(polydata, self.selected_normal, centroid)
|
||||||
transform = vtk.vtkTransform()
|
projected_points = projected_polydata.GetPoints()
|
||||||
transform.Identity()
|
print("proj_points", projected_points)
|
||||||
|
|
||||||
# Translate to center the transform on the centroid
|
# Extract 2D coordinates
|
||||||
transform.Translate(-centroid[0], -centroid[1], -centroid[2])
|
self.project_tosketch_edge = self.compute_2d_coordinates(projected_polydata, self.selected_normal)
|
||||||
|
print("3d_points_proj", self.project_tosketch_edge)
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Create a mapper and actor for the projected data
|
# Create a mapper and actor for the projected data
|
||||||
mapper = vtk.vtkPolyDataMapper()
|
mapper = vtk.vtkPolyDataMapper()
|
||||||
@ -324,41 +387,14 @@ class VTKWidget(QtWidgets.QWidget):
|
|||||||
# Add the actor to the scene
|
# Add the actor to the scene
|
||||||
self.renderer.AddActor(actor)
|
self.renderer.AddActor(actor)
|
||||||
|
|
||||||
# Add a plane to visualize the projection plane
|
if len(self.selected_edges) > 2:
|
||||||
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
|
|
||||||
self.selected_edges = []
|
self.selected_edges = []
|
||||||
else:
|
self.selected_normal = []
|
||||||
print("Selected cell is not a line")
|
|
||||||
|
|
||||||
# Render the scene
|
# Render the scene
|
||||||
self.vtk_widget.GetRenderWindow().Render()
|
self.vtk_widget.GetRenderWindow().Render()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.interactor.Initialize()
|
self.interactor.Initialize()
|
||||||
self.interactor.Start()
|
self.interactor.Start()
|
||||||
|
53
main.py
53
main.py
@ -295,7 +295,20 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
#Create and draw Interactor
|
#Create and draw Interactor
|
||||||
geo = Geometry()
|
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}"
|
name_op = f"extrd-{name}"
|
||||||
element = {
|
element = {
|
||||||
@ -316,7 +329,6 @@ class MainWindow(QMainWindow):
|
|||||||
name = self.ui.body_list.currentItem().text()
|
name = self.ui.body_list.currentItem().text()
|
||||||
points = self.model['operation'][name]['sdf_object']
|
points = self.model['operation'][name]['sdf_object']
|
||||||
self.list_selected.append(points)
|
self.list_selected.append(points)
|
||||||
#print(self.list_selected)
|
|
||||||
|
|
||||||
if len(self.list_selected) > 1:
|
if len(self.list_selected) > 1:
|
||||||
geo = Geometry()
|
geo = Geometry()
|
||||||
@ -332,8 +344,8 @@ class MainWindow(QMainWindow):
|
|||||||
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.sketch_list.findItems(name_op, Qt.MatchExactly)
|
||||||
self.ui.body_list.setCurrentItem(items[-1])
|
#self.ui.body_list.setCurrentItem(items[-1])
|
||||||
#self.draw_mesh()
|
self.draw_mesh()
|
||||||
else:
|
else:
|
||||||
print("mindestens 2!")
|
print("mindestens 2!")
|
||||||
|
|
||||||
@ -342,17 +354,46 @@ class MainWindow(QMainWindow):
|
|||||||
self.custom_3D_Widget.update()
|
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)
|
||||||
|
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):
|
def distance(self, p1, p2):
|
||||||
"""Calculate the distance between two points."""
|
"""Calculate the distance between two points."""
|
||||||
print("p1", p1)
|
print("p1", p1)
|
||||||
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):
|
def extrude_shape(self, points, length: float, angle, normal):
|
||||||
"""2D to 3D sdf always first"""
|
"""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
|
return f
|
||||||
|
|
||||||
|
def mirror_body(self, sdf_object3d):
|
||||||
|
f = sdf_object3d.rotate(pi)
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
def cut_shapes(self, sdf_object1, sdf_object2):
|
def cut_shapes(self, sdf_object1, sdf_object2):
|
||||||
f = difference(sdf_object1, sdf_object2) # equivalent
|
f = difference(sdf_object1, sdf_object2) # equivalent
|
||||||
return f
|
return f
|
||||||
|
@ -32,10 +32,10 @@ def create_3D(lines, z_origin, depth):
|
|||||||
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_origin + depth
|
||||||
|
|
||||||
xe, ye = end
|
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
|
line3d_orig = coordinate3d_start_orig, coordinate3d_end_orig
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user