384 lines
14 KiB
Python
384 lines
14 KiB
Python
import uuid
|
|
import names
|
|
from PySide6.QtCore import Qt, QPoint
|
|
from PySide6.QtWidgets import QApplication, QMainWindow, QSizePolicy, QInputDialog
|
|
from Gui import Ui_fluencyCAD # Import the generated GUI module
|
|
from drawing_modules.vtk_widget import VTKWidget
|
|
from drawing_modules.vysta_widget import PyVistaWidget
|
|
from drawing_modules.draw_widget2d import SketchWidget
|
|
from sdf import *
|
|
from python_solvespace import SolverSystem, ResultFlag
|
|
from mesh_modules import simple_mesh, vesta_mesh, interactor_mesh
|
|
|
|
|
|
# main, draw_widget, gl_widget
|
|
|
|
class MainWindow(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
# Set up the UI from the generated GUI module
|
|
self.ui = Ui_fluencyCAD()
|
|
self.ui.setupUi(self)
|
|
|
|
self.custom_3D_Widget = VTKWidget()
|
|
layout = self.ui.gl_box.layout()
|
|
layout.addWidget(self.custom_3D_Widget)
|
|
size_policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
|
#self.custom_3D_Widget.setSizePolicy(size_policy)
|
|
|
|
self.sketchWidget = SketchWidget()
|
|
layout2 = self.ui.sketch_tab.layout() # Get the layout of self.ui.gl_canvas
|
|
layout2.addWidget(self.sketchWidget)
|
|
size_policy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
|
|
self.sketchWidget.setSizePolicy(size_policy)
|
|
|
|
### Main Model
|
|
self.model = {
|
|
'sketch': {},
|
|
'operation': {},
|
|
}
|
|
self.list_selected = []
|
|
|
|
#self.ui.pb_apply_code.pressed.connect(self.check_current_tab)
|
|
self.ui.sketch_list.currentItemChanged.connect(self.on_item_changed)
|
|
self.ui.sketch_list.itemChanged.connect(self.draw_mesh)
|
|
|
|
### Sketches
|
|
self.ui.pb_origin_wp.pressed.connect(self.add_new_sketch_origin)
|
|
self.ui.pb_origin_face.pressed.connect(self.add_new_sketch_wp)
|
|
|
|
self.ui.pb_nw_sktch.pressed.connect(self.add_sketch)
|
|
self.ui.pb_del_sketch.pressed.connect(self.del_sketch)
|
|
self.ui.pb_edt_sktch.pressed.connect(self.edit_sketch)
|
|
|
|
###Modes
|
|
self.ui.pb_linetool.pressed.connect(self.act_line_mode)
|
|
self.ui.pb_con_ptpt.pressed.connect(self.act_constrain_pt_pt_mode)
|
|
self.ui.pb_con_line.pressed.connect(self.act_constrain_pt_line_mode)
|
|
self.ui.pb_con_horiz.pressed.connect(self.act_constrain_horiz_line_mode)
|
|
self.ui.pb_con_vert.pressed.connect(self.act_constrain_vert_line_mode)
|
|
self.ui.pb_con_dist.pressed.connect(self.act_constrain_distance_mode)
|
|
self.ui.pb_con_mid.pressed.connect(self.act_constrain_mid_point_mode)
|
|
|
|
### Operations
|
|
self.ui.pb_extrdop.pressed.connect(self.send_extrude)
|
|
self.ui.pb_cutop.pressed.connect(self.send_cut)
|
|
self.ui.pb_del_body.pressed.connect(self.del_body)
|
|
|
|
self.sketchWidget.constrain_done.connect(self.draw_op_complete)
|
|
|
|
def add_new_sketch_origin(self):
|
|
self.sketchWidget.clear_sketch()
|
|
self.sketchWidget.create_workplane()
|
|
|
|
def add_new_sketch_wp(self):
|
|
self.sketchWidget.clear_sketch()
|
|
edges = [((-158.0, -20.0, -25.0), (286.0, -195.0, -25.0)), ((-158.0, -20.0, 25.0), (-158.0, -20.0, -25.0))]
|
|
#edges = self.custom_3D_Widget.access_selected_points
|
|
normal = self.custom_3D_Widget.selected_normal
|
|
|
|
self.sketchWidget.create_workplane_projected()
|
|
self.sketchWidget.create_proj_lines(edges)
|
|
|
|
#self.sketchWidget.create_workplane_space(edges, normal)
|
|
|
|
def act_line_mode(self):
|
|
if not self.ui.pb_linetool.isChecked():
|
|
self.sketchWidget.mouse_mode = 'line'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
self.sketchWidget.line_draw_buffer = [None, None]
|
|
|
|
def act_constrain_pt_pt_mode(self):
|
|
if not self.ui.pb_con_ptpt.isChecked():
|
|
self.sketchWidget.mouse_mode = 'pt_pt'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def act_constrain_pt_line_mode(self):
|
|
if not self.ui.pb_con_line.isChecked():
|
|
self.sketchWidget.mouse_mode = 'pt_line'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def act_constrain_horiz_line_mode(self):
|
|
if not self.ui.pb_con_horiz.isChecked():
|
|
self.sketchWidget.mouse_mode = 'horiz'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def act_constrain_vert_line_mode(self):
|
|
if not self.ui.pb_con_vert.isChecked():
|
|
self.sketchWidget.mouse_mode = 'vert'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def act_constrain_distance_mode(self):
|
|
if not self.ui.pb_con_dist.isChecked():
|
|
self.sketchWidget.mouse_mode = 'distance'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def act_constrain_mid_point_mode(self):
|
|
if not self.ui.pb_con_mid.isChecked():
|
|
self.sketchWidget.mouse_mode = 'pb_con_mid'
|
|
else:
|
|
self.sketchWidget.mouse_mode = None
|
|
|
|
def draw_op_complete(self):
|
|
# safely disable the line modes
|
|
self.ui.pb_linetool.setChecked(False)
|
|
self.ui.pb_con_ptpt.setChecked(False)
|
|
self.ui.pb_con_line.setChecked(False)
|
|
self.ui.pb_con_dist.setChecked(False)
|
|
self.ui.pb_con_mid.setChecked(False)
|
|
self.ui.pb_con_perp.setChecked(False)
|
|
|
|
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)
|
|
model = self.model['operation'][name]['sdf_object']
|
|
|
|
vesta = vesta_mesh
|
|
model_data = vesta.generate_mesh_from_sdf(model, resolution=64, threshold=0)
|
|
|
|
vertices, faces = model_data
|
|
vesta.save_mesh_as_stl(vertices, faces, 'test.stl')
|
|
self.custom_3D_Widget.render_from_points_direct_with_faces(vertices, faces)
|
|
|
|
def on_item_changed(self, current_item, previous_item):
|
|
if current_item:
|
|
name = current_item.text()
|
|
#self.view_update()
|
|
print(f"Selected item: {name}")
|
|
|
|
def convert_points_for_sdf(self):
|
|
points_for_sdf = []
|
|
for point_to_poly in self.sketchWidget.slv_points_main:
|
|
points_for_sdf.append(self.translate_points_tup(point_to_poly['ui_point']))
|
|
|
|
return points_for_sdf
|
|
|
|
def convert_lines_for_interactor(self):
|
|
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)
|
|
line = start_draw, end_draw
|
|
points_for_interact.append(line)
|
|
|
|
print("packed_lines", points_for_interact)
|
|
|
|
return points_for_interact
|
|
|
|
def add_sketch(self):
|
|
|
|
name = f"sketch-{str(names.get_first_name())}"
|
|
points_for_sdf = self.convert_points_for_sdf()
|
|
|
|
element = {
|
|
'id': name,
|
|
'type': 'sketch',
|
|
'point_list': self.sketchWidget.slv_points_main,
|
|
'line_list': self.sketchWidget.slv_lines_main,
|
|
'sketch_points': points_for_sdf,
|
|
'solver': self.sketchWidget.solv
|
|
}
|
|
|
|
self.model['sketch'][element['id']] = element
|
|
print(self.model)
|
|
|
|
self.ui.sketch_list.addItem(name)
|
|
self.ui.pb_linetool.setChecked(False)
|
|
self.sketchWidget.line_mode = False
|
|
|
|
items = self.ui.sketch_list.findItems(name, Qt.MatchExactly)[0]
|
|
self.ui.sketch_list.setCurrentItem(items)
|
|
|
|
def edit_sketch(self):
|
|
name = self.ui.sketch_list.currentItem().text()
|
|
#self.sketchWidget.clear_sketch()
|
|
|
|
self.sketchWidget.slv_points_main = self.model['sketch'][name]['point_list']
|
|
self.sketchWidget.slv_lines_main = self.model['sketch'][name]['line_list']
|
|
self.sketchWidget.solv = self.model['sketch'][name]['solver']
|
|
|
|
self.sketchWidget.update()
|
|
print("model",self.model)
|
|
print("widget", self.sketchWidget.slv_points_main)
|
|
|
|
def del_sketch(self):
|
|
print("Deleting")
|
|
name = self.ui.sketch_list.currentItem() # Get the current item
|
|
|
|
print(self.model)
|
|
|
|
if name is not None:
|
|
item_name = name.text()
|
|
print("obj_name", item_name)
|
|
|
|
# Check if the 'sketch' key exists in the model dictionary
|
|
if 'sketch' in self.model and item_name in self.model['sketch']:
|
|
if self.model['sketch'][item_name]['id'] == item_name:
|
|
row = self.ui.sketch_list.row(name) # Get the row of the current item
|
|
self.ui.sketch_list.takeItem(row) # Remove the item from the list widget
|
|
self.sketchWidget.clear_sketch()
|
|
self.model['sketch'].pop(item_name) # Remove the item from the sketch dictionary
|
|
print(f"Removed sketch: {item_name}")
|
|
|
|
# Check if the 'operation' key exists in the model dictionary
|
|
elif 'operation' in self.model and item_name in self.model['operation']:
|
|
if self.model['operation'][item_name]['id'] == item_name:
|
|
row = self.ui.sketch_list.row(name) # Get the row of the current item
|
|
self.ui.sketch_list.takeItem(row) # Remove the item from the list widget
|
|
self.sketchWidget.clear_sketch()
|
|
self.model['operation'].pop(item_name) # Remove the item from the operation dictionary
|
|
print(f"Removed operation: {item_name}")
|
|
|
|
else:
|
|
print(f"Item '{item_name}' not found in either 'sketch' or 'operation' dictionary.")
|
|
else:
|
|
print("No item selected.")
|
|
|
|
def update_body(self):
|
|
pass
|
|
|
|
def del_body(self):
|
|
print("Deleting")
|
|
name = self.ui.body_list.currentItem() # Get the current item
|
|
|
|
if name is not None:
|
|
item_name = name.text()
|
|
print("obj_name", item_name)
|
|
# Check if the 'operation' key exists in the model dictionary
|
|
|
|
if 'operation' in self.model and item_name in self.model['operation']:
|
|
if self.model['operation'][item_name]['id'] == item_name:
|
|
row = self.ui.body_list.row(name) # Get the row of the current item
|
|
self.ui.body_list.takeItem(row) # Remove the item from the list widget
|
|
self.model['operation'].pop(item_name) # Remove the item from the operation dictionary
|
|
print(f"Removed operation: {item_name}")
|
|
self.custom_3D_Widget.clear_mesh()
|
|
|
|
def translate_points_tup(self, point: QPoint):
|
|
"""QPoints from Display to mesh data
|
|
input: Qpoints
|
|
output: Tuple X,Y
|
|
"""
|
|
if isinstance(point, QPoint):
|
|
return point.x(), point.y()
|
|
|
|
def send_extrude(self):
|
|
selected = self.ui.sketch_list.currentItem()
|
|
name = selected.text()
|
|
points = self.model['sketch'][name]['sketch_points']
|
|
lines = self.convert_lines_for_interactor()
|
|
|
|
if points[-1] == points[0]:
|
|
#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
|
|
|
|
#Create and draw Interactor
|
|
geo = Geometry()
|
|
f = geo.extrude_shape(points, length)
|
|
|
|
name_op = f"extrd-{name}"
|
|
element = {
|
|
'id': name_op,
|
|
'type': 'extrude',
|
|
'sdf_object': f,
|
|
}
|
|
#print(element)
|
|
|
|
self.model['operation'][name_op] = element
|
|
self.ui.body_list.addItem(name_op)
|
|
items = self.ui.body_list.findItems(name_op, Qt.MatchExactly)[0]
|
|
self.ui.body_list.setCurrentItem(items)
|
|
self.calc_sketch_projection_3d(lines, 0, length)
|
|
#self.draw_mesh()
|
|
|
|
def send_cut(self):
|
|
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()
|
|
f = geo.cut_shapes(self.list_selected[0], self.list_selected[1] )
|
|
|
|
element = {
|
|
'id': name,
|
|
'type': 'cut',
|
|
'sdf_object': f,
|
|
}
|
|
|
|
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])
|
|
#self.draw_mesh()
|
|
else:
|
|
print("mindestens 2!")
|
|
|
|
def load_and_render(self, file):
|
|
self.custom_3D_Widget.load_stl(file)
|
|
self.custom_3D_Widget.update()
|
|
|
|
class Geometry:
|
|
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):
|
|
"""2D to 3D sdf always first"""
|
|
f = polygon(points).extrude(length)
|
|
return f
|
|
|
|
def cut_shapes(self, sdf_object1, sdf_object2):
|
|
f = difference(sdf_object1, sdf_object2) # equivalent
|
|
return f
|
|
|
|
def export_mesh(self, sdf_object):
|
|
"""FINAL EXPORT"""
|
|
result_points = sdf_object.generate()
|
|
write_binary_stl('out.stl', result_points)
|
|
|
|
def generate_mesh_from_code(self, code_text: str):
|
|
local_vars = {}
|
|
|
|
try:
|
|
print(code_text)
|
|
exec(code_text, globals(), local_vars)
|
|
# Retrieve the result from the captured local variables
|
|
result = local_vars.get('result')
|
|
print("Result:", result)
|
|
|
|
except Exception as e:
|
|
print("Error executing code:", e)
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication([])
|
|
window = MainWindow()
|
|
window.show()
|
|
app.exec()
|
|
|
|
#pyside6-uic gui.ui > Gui.py -g python |