Merge pull request 'structure' (#7) from structure into master

Reviewed-on: BKLronin/fluency#7
This commit is contained in:
BKLronin 2025-06-01 10:05:29 +02:00
commit 0a9d557ce0
7 changed files with 391 additions and 180 deletions

31
Gui.py
View File

@ -419,6 +419,7 @@ class Ui_fluencyCAD(object):
self.pb_con_perp = QPushButton(self.groupBox_3)
self.pb_con_perp.setObjectName(u"pb_con_perp")
self.pb_con_perp.setCheckable(True)
self.pb_con_perp.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_perp, 1, 1, 1, 1)
@ -454,6 +455,7 @@ class Ui_fluencyCAD(object):
self.pb_con_mid = QPushButton(self.groupBox_3)
self.pb_con_mid.setObjectName(u"pb_con_mid")
self.pb_con_mid.setCheckable(True)
self.pb_con_mid.setAutoExclusive(False)
self.gridLayout_4.addWidget(self.pb_con_mid, 1, 0, 1, 1)
@ -469,11 +471,11 @@ class Ui_fluencyCAD(object):
self.tabWidget.setSizePolicy(sizePolicy5)
self.tabWidget.setMaximumSize(QSize(200, 16777215))
self.tabWidget.setTabPosition(QTabWidget.South)
self.widget = QWidget()
self.widget.setObjectName(u"widget")
self.verticalLayout_3 = QVBoxLayout(self.widget)
self.snaps = QWidget()
self.snaps.setObjectName(u"snaps")
self.verticalLayout_3 = QVBoxLayout(self.snaps)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.groupBox_5 = QGroupBox(self.widget)
self.groupBox_5 = QGroupBox(self.snaps)
self.groupBox_5.setObjectName(u"groupBox_5")
sizePolicy6 = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
sizePolicy6.setHorizontalStretch(0)
@ -491,6 +493,7 @@ class Ui_fluencyCAD(object):
self.pb_snap_vert = QPushButton(self.groupBox_5)
self.pb_snap_vert.setObjectName(u"pb_snap_vert")
self.pb_snap_vert.setCheckable(True)
self.pb_snap_vert.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pb_snap_vert, 2, 1, 1, 1)
@ -516,12 +519,14 @@ class Ui_fluencyCAD(object):
self.pushButton_7 = QPushButton(self.groupBox_5)
self.pushButton_7.setObjectName(u"pushButton_7")
self.pushButton_7.setCheckable(True)
self.pushButton_7.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pushButton_7, 3, 0, 1, 1)
self.pb_snap_horiz = QPushButton(self.groupBox_5)
self.pb_snap_horiz.setObjectName(u"pb_snap_horiz")
self.pb_snap_horiz.setCheckable(True)
self.pb_snap_horiz.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pb_snap_horiz, 2, 0, 1, 1)
@ -534,28 +539,32 @@ class Ui_fluencyCAD(object):
self.pushButton_8 = QPushButton(self.groupBox_5)
self.pushButton_8.setObjectName(u"pushButton_8")
self.pushButton_8.setCheckable(True)
self.pushButton_8.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pushButton_8, 0, 0, 1, 1)
self.pb_snap_midp = QPushButton(self.groupBox_5)
self.pb_snap_midp.setObjectName(u"pb_snap_midp")
self.pb_snap_midp.setCheckable(True)
self.pb_snap_midp.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pb_snap_midp, 0, 1, 1, 1)
self.pb_snap_angle = QPushButton(self.groupBox_5)
self.pb_snap_angle.setObjectName(u"pb_snap_angle")
self.pb_snap_angle.setCheckable(True)
self.pb_snap_angle.setAutoExclusive(False)
self.gridLayout_11.addWidget(self.pb_snap_angle, 3, 1, 1, 1)
self.verticalLayout_3.addWidget(self.groupBox_5)
self.tabWidget.addTab(self.widget, "")
self.widget1 = QWidget()
self.widget1.setObjectName(u"widget1")
self.tabWidget.addTab(self.widget1, "")
self.tabWidget.addTab(self.snaps, "")
self.settings = QWidget()
self.settings.setObjectName(u"settings")
self.tabWidget.addTab(self.settings, "")
self.gridLayout.addWidget(self.tabWidget, 3, 0, 1, 1)
@ -582,7 +591,7 @@ class Ui_fluencyCAD(object):
self.retranslateUi(fluencyCAD)
self.InputTab.setCurrentIndex(0)
self.tabWidget.setCurrentIndex(1)
self.tabWidget.setCurrentIndex(0)
QMetaObject.connectSlotsByName(fluencyCAD)
@ -710,8 +719,8 @@ class Ui_fluencyCAD(object):
self.pushButton_8.setText(QCoreApplication.translate("fluencyCAD", u"Pnt", None))
self.pb_snap_midp.setText(QCoreApplication.translate("fluencyCAD", u"MidP", None))
self.pb_snap_angle.setText(QCoreApplication.translate("fluencyCAD", u"Angles", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.widget), QCoreApplication.translate("fluencyCAD", u"Setg 1", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.widget1), QCoreApplication.translate("fluencyCAD", u"Setg 2", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.snaps), QCoreApplication.translate("fluencyCAD", u"Setg 1", None))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.settings), QCoreApplication.translate("fluencyCAD", u"Setg 2", None))
self.menuFile.setTitle(QCoreApplication.translate("fluencyCAD", u"File", None))
self.menuSettings.setTitle(QCoreApplication.translate("fluencyCAD", u"Settings", None))
# retranslateUi

View File

@ -32,4 +32,4 @@ So far these are the elements:
- Code: A special type that directly builds bodys from sdfCAD code.
- Body: The 3D meshed result from sdfCAD
- Sketch: The base to draw new entities.
- Interactor: A special component mesh that is used to manipulate the bodys in 3d view.
- Interactor (edges): A special component mesh that is used to manipulate the bodys in 3d view.

View File

@ -24,15 +24,60 @@ class SketchWidget(QWidget):
self.hovered_point = None
self.selected_line = None
### Display Settings
self.snapping_range = 20 # Range in pixels for snapping
self.zoom = 1
# Mouse Input
self.setMouseTracking(True)
self.mouse_mode = False
self.is_construct = False
self.snap_mode = {
"point": False,
"mpoint": False,
"horiz": False,
"vert":False,
"grid": False,
"angle": False
}
# Solver
self.solv = SolverSystem()
self.sketch = Sketch2d()
def act_line_mode(self, checked):
if checked:
self.mouse_mode = 'line'
print(self.mouse_mode)
else:
self.mouse_mode = None
def act_constrain_pt_pt_mode(self):
self.mouse_mode = 'pt_pt'
def act_constrain_pt_line_mode(self):
self.mouse_mode = 'pt_line'
def act_constrain_horiz_line_mode(self):
self.mouse_mode = 'horiz'
def act_constrain_vert_line_mode(self):
self.mouse_mode = 'vert'
def act_constrain_distance_mode(self):
self.mouse_mode = 'distance'
def act_constrain_mid_point_mode(self):
self.mouse_mode = 'pb_con_mid'
def on_snap_mode_change(self, helper_type: str, value: bool):
self.snap_mode[helper_type] = value
def on_construct_change(self, checked):
self.is_construct = checked
def create_sketch(self, sketch_in ):
self.sketch = Sketch2d()
self.sketch.id = sketch_in.id
@ -46,6 +91,7 @@ class SketchWidget(QWidget):
return self.sketch
def reset_buffers(self):
self.mouse_mode = None
self.line_draw_buffer = [None, None]
self.drag_buffer = [None, None]
self.main_buffer = [None, None]
@ -235,11 +281,12 @@ class SketchWidget(QWidget):
Go through solversystem and check points2d for changes in position after solving
:return: List with points that now have a different position
"""
old_points_ui = []
new_points_ui = []
for old_point_ui in self.sketch.points:
old_points_ui.append(old_point_ui.ui_point)
for index, old_point_ui in enumerate(self.sketch.points):
old_points_ui.append((index, old_point_ui.ui_point))
for i in range(self.sketch.entity_len()):
# Iterate though full length because mixed list from SS
@ -255,9 +302,10 @@ class SketchWidget(QWidget):
if len(old_points_ui) != len(new_points_ui):
print(f"Length mismatch {len(old_points_ui)} - {len(new_points_ui)}")
for index, (old_point, new_point) in enumerate(zip(old_points_ui, new_points_ui)):
for (old_index, old_point), new_point in zip(old_points_ui, new_points_ui):
if old_point != new_point:
differences.append((index, old_point, new_point))
#print(old_point)
differences.append((old_index, old_point, new_point))
return differences
@ -266,13 +314,20 @@ class SketchWidget(QWidget):
# print("Initial slv_points_main:", self.slv_points_main)
print("Change list:", point_list)
if len(point_list) > 0:
"""for point in point_list:
new = Point2D(point.x(), point.y())
self.sketch.points.append(new)"""
if len(point_list) > 1:
for tbu_points_idx in point_list:
# Each tbu_points_idx is a tuple: (index, old_point, new_point)
index, old_point, new_point = tbu_points_idx
# Update the point in slv_points_main
self.sketch.points[index].ui_point = new_point
"""for points in self.sketch.points:
print(points.ui_point)"""
# Print updated state
# print("Updated slv_points_main:", self.slv_points_main)
@ -291,7 +346,7 @@ class SketchWidget(QWidget):
if event.button() == Qt.LeftButton and not self.mouse_mode:
self.drag_buffer[1] = local_event_pos
print("Le main buffer", self.drag_buffer)
#print("Le main buffer", self.drag_buffer)
if not None in self.main_buffer and len(self.main_buffer) == 2:
entry = self.drag_buffer[0]
@ -300,9 +355,9 @@ class SketchWidget(QWidget):
self.sketch.solve()
points_need_update = self.check_all_points()
self.update_ui_points(points_need_update)
self.check_all_lines_and_update(points_need_update)
#points_need_update = self.check_all_points()
#self.update_ui_points(points_need_update)
#self.check_all_lines_and_update(points_need_update)
self.update()
self.drag_buffer = [None, None]
@ -315,6 +370,7 @@ class SketchWidget(QWidget):
self.drag_buffer[0] = self.get_handle_from_ui_point(self.hovered_point)
if event.button() == Qt.RightButton and self.mouse_mode:
self.constrain_done.emit()
self.reset_buffers()
if event.button() == Qt.LeftButton and self.mouse_mode == "line":
@ -329,6 +385,10 @@ class SketchWidget(QWidget):
v = clicked_pos.y()
point = Point2D(u,v)
print("construct", self.is_construct )
# Construction mode
point.is_helper = self.is_construct
self.sketch.add_point(point)
self.line_draw_buffer[0] = point
@ -337,17 +397,25 @@ class SketchWidget(QWidget):
u = clicked_pos.x()
v = clicked_pos.y()
print("construct", self.is_construct)
point = Point2D(u, v)
# Construction mode
point.is_helper = self.is_construct
self.sketch.add_point(point)
self.line_draw_buffer[1] = point
print("Buffer state", self.line_draw_buffer)
#print("Buffer state", self.line_draw_buffer)
if self.line_draw_buffer[0] and self.line_draw_buffer[1]:
line = Line2D(self.line_draw_buffer[0], self.line_draw_buffer[1])
# Construction mode
line.is_helper = self.is_construct
self.sketch.add_line(line)
# Reset the buffer for the next line segment
@ -367,7 +435,7 @@ class SketchWidget(QWidget):
self.main_buffer[1] = self.get_handle_from_ui_point(self.hovered_point)
if self.main_buffer[0] and self.main_buffer[1]:
print("buf", self.main_buffer)
# print("buf", self.main_buffer)
self.sketch.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.wp)
@ -387,7 +455,7 @@ class SketchWidget(QWidget):
self.main_buffer = [None, None]
if event.button() == Qt.LeftButton and self.mouse_mode == "pt_line":
print("ptline")
#print("ptline")
line_selected = None
if self.hovered_point and not self.main_buffer[1]:
@ -418,9 +486,10 @@ class SketchWidget(QWidget):
self.main_buffer = [None, None]
if event.button() == Qt.LeftButton and self.mouse_mode == "pb_con_mid":
print("ptline")
#print("ptline")
line_selected = None
if self.hovered_point and not self.main_buffer[1]:
self.main_buffer[0] = self.get_handle_from_ui_point(self.hovered_point)
@ -433,6 +502,12 @@ class SketchWidget(QWidget):
if self.sketch.solve() == ResultFlag.OKAY:
print("Fuck yeah")
for idx, line in enumerate(self.sketch.lines):
# print(line.crd1.ui_point)
if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point):
self.sketch.lines[idx].constraints.append("mid")
print(self.sketch.lines[idx].constraints)
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
@ -450,12 +525,21 @@ class SketchWidget(QWidget):
line_selected = self.get_line_handle_from_ui_point(local_event_pos)
if line_selected:
self.sketch.horizontal(line_selected, self.sketch.wp)
if self.sketch.solve() == ResultFlag.OKAY:
print("Fuck yeah")
# Add succesful constraint to constrain draw list so it gets drawn in paint function
for idx, line in enumerate(self.sketch.lines):
#print(line.crd1.ui_point)
if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point):
self.sketch.lines[idx].constraints.append("hrz")
#print(self.sketch.lines[idx].constraints)
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
@ -473,6 +557,11 @@ class SketchWidget(QWidget):
if self.sketch.solve() == ResultFlag.OKAY:
print("Fuck yeah")
for idx, line in enumerate(self.sketch.lines):
# print(line.crd1.ui_point)
if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point):
self.sketch.lines[idx].constraints.append("vrt")
# print(self.sketch.lines[idx].constraints)
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
@ -490,7 +579,7 @@ class SketchWidget(QWidget):
e2 = None
if self.hovered_point:
print("buf point")
# print("buf point")
# Get the point as UI point as buffer
self.main_buffer[0] = self.hovered_point
@ -514,6 +603,11 @@ class SketchWidget(QWidget):
if self.sketch.solve() == ResultFlag.OKAY:
print("Fuck yeah")
for idx, line in enumerate(self.sketch.lines):
# print(line.crd1.ui_point)
if self.is_point_on_line(local_event_pos, line.crd1.ui_point, line.crd2.ui_point):
if "dist" not in self.sketch.lines[idx].constraints:
self.sketch.lines[idx].constraints.append("dst")
elif self.sketch.solve() == ResultFlag.DIDNT_CONVERGE:
print("Solve_failed - Converge")
@ -532,6 +626,8 @@ class SketchWidget(QWidget):
self.update_ui_points(points_need_update)
self.check_all_lines_and_update(points_need_update)
self.update()
def mouseMoveEvent(self, event):
@ -540,7 +636,7 @@ class SketchWidget(QWidget):
closest_point = None
min_distance = float('inf')
threshold = 10 # Distance threshold for highlighting
threshold = 15 # Distance threshold for highlighting
if self.mouse_mode == "line" and self.line_draw_buffer[0]:
# Update the current cursor position as the second point
@ -562,7 +658,7 @@ class SketchWidget(QWidget):
if closest_point != self.hovered_point:
self.hovered_point = closest_point
print(self.hovered_point)
#print(self.hovered_point)
for line in self.sketch.lines:
p1 = line.crd1.ui_point
@ -571,12 +667,15 @@ class SketchWidget(QWidget):
if self.is_point_on_line(local_event_pos, p1, p2):
self.selected_line = p1, p2
if self.snap_mode.get("mpoint"):
# Midpointsnap only in drawer not solver
mid = self.calculate_midpoint(p1, p2)
distance = (local_event_pos - mid).manhattanLength()
if distance < threshold and distance < min_distance:
self.hovered_point = mid
break
break
else:
self.selected_line = None
@ -678,6 +777,103 @@ class SketchWidget(QWidget):
widget_y = -point.y()
return QPoint(int(widget_x), int(widget_y))
def draw_measurement(self,painter, start_point, end_point):
pen_normal = QPen(Qt.gray)
pen_normal.setWidthF(2 / self.zoom)
pen_planned = QPen(Qt.gray)
pen_planned.setStyle(Qt.PenStyle.DotLine)
pen_planned.setWidthF(2 / self.zoom)
pen_construct = QPen(Qt.cyan)
pen_construct.setStyle(Qt.PenStyle.DotLine)
pen_construct.setWidthF(1 / self.zoom)
pen_solver = QPen(Qt.green)
pen_solver.setWidthF(2 / self.zoom)
pen_text = QPen(Qt.white)
pen_text.setWidthF(1 / self.zoom)
# Calculate the direction of the line
dx = end_point.x() - start_point.x()
dy = end_point.y() - start_point.y()
# Swap and negate to get a perpendicular vector
perp_dx = -dy
perp_dy = dx
# Normalize the perpendicular vector
length = (perp_dx ** 2 + perp_dy ** 2) ** 0.5
if length == 0: # Prevent division by zero
return
perp_dx /= length
perp_dy /= length
# Fixed length for the perpendicular lines
fixed_length = 40 # Adjust as needed
half_length = fixed_length # fixed_length / 2
# Calculate endpoints for the perpendicular line at the start
start_perp_start_x = start_point.x() + perp_dx
start_perp_start_y = start_point.y() + perp_dy
start_perp_end_x = start_point.x() + perp_dx * half_length * (1 / self.zoom)
start_perp_end_y = start_point.y() + perp_dy * half_length * (1 / self.zoom)
start_perp_end_x_conn_line = start_point.x() + perp_dx * half_length * 0.75 * (1 / self.zoom)
start_perp_end_y_conn_line = start_point.y() + perp_dy * half_length * 0.75 * (1 / self.zoom)
# Draw the perpendicular line at the start
painter.setPen(pen_construct) # Different color for the perpendicular line
painter.drawLine(
QPointF(start_perp_start_x, start_perp_start_y),
QPointF(start_perp_end_x, start_perp_end_y)
)
# Calculate endpoints for the perpendicular line at the end
end_perp_start_x = end_point.x() + perp_dx
end_perp_start_y = end_point.y() + perp_dy
end_perp_end_x = end_point.x() + perp_dx * half_length * (1 / self.zoom)
end_perp_end_y = end_point.y() + perp_dy * half_length * (1 / self.zoom)
end_perp_end_x_conn_line = end_point.x() + perp_dx * half_length * .75 * (1 / self.zoom)
end_perp_end_y_conn_line = end_point.y() + perp_dy * half_length * .75 * (1 / self.zoom)
# Draw the perpendicular line at the end
painter.drawLine(
QPointF(end_perp_start_x, end_perp_start_y),
QPointF(end_perp_end_x, end_perp_end_y)
)
painter.drawLine(
QPointF(end_perp_end_x_conn_line, end_perp_end_y_conn_line),
QPointF(start_perp_end_x_conn_line, start_perp_end_y_conn_line)
)
# Save painter state
painter.save()
painter.setPen(pen_text)
# Calculate the distance and midpoint
dis = self.distance(start_point, end_point)
mid = self.calculate_midpoint(QPointF(start_perp_end_x_conn_line, start_perp_end_y_conn_line),
QPointF(end_perp_end_x_conn_line, end_perp_end_y_conn_line))
# mid = self.calculate_midpoint(start_point, end_point)
# Transform for text
painter.translate(mid.x(), mid.y()) # Move to the midpoint
painter.scale(1, -1) # Flip y-axis back to make text readable
# Draw the text
painter.drawText(0, 0, str(round(dis, 2))) # Draw text at transformed position
# Restore painter state
painter.restore()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
@ -700,6 +896,10 @@ class SketchWidget(QWidget):
pen_normal = QPen(Qt.gray)
pen_normal.setWidthF(2 / self.zoom)
pen_planned = QPen(Qt.gray)
pen_planned.setStyle(Qt.PenStyle.DotLine)
pen_planned.setWidthF(2 / self.zoom)
pen_construct = QPen(Qt.cyan)
pen_construct.setStyle(Qt.PenStyle.DotLine)
pen_construct.setWidthF(1 / self.zoom)
@ -726,26 +926,10 @@ class SketchWidget(QWidget):
if self.mouse_mode == "line" and self.line_draw_buffer[0] and self.dynamic_line_end is not None:
start_point = self.line_draw_buffer[0].ui_point
end_point = self.dynamic_line_end
painter.setPen(Qt.red) # Use a different color for the dynamic line
painter.setPen(pen_planned) # Use a different color for the dynamic line
painter.drawLine(start_point, end_point)
# Save painter state
painter.save()
painter.setPen(pen_text)
# Calculate the distance and midpoint
dis = self.distance(start_point, end_point)
mid = self.calculate_midpoint(start_point, end_point)
# Transform for text
painter.translate(mid.x(), mid.y()) # Move to the midpoint
painter.scale(1, -1) # Flip y-axis back to make text readable
# Draw the text
painter.drawText(0, 0, str(round(dis, 2))) # Draw text at transformed position
# Restore painter state
painter.restore()
self.draw_measurement(painter, start_point, end_point)
for line in self.sketch.lines:
if line.is_helper:
@ -759,6 +943,17 @@ class SketchWidget(QWidget):
p2 = line.crd2.ui_point
painter.drawLine(p1, p2)
if not self.selected_line:
painter.save()
midp = self.calculate_midpoint(p1, p2)
painter.translate(midp)
painter.scale(1, -1)
for i, text in enumerate(line.constraints):
painter.drawText(0, i * 15, f"> {text} <")
painter.restore()
# Draw all solver points
if self.sketch.entity_len():
painter.setPen(pen_solver)
@ -782,6 +977,9 @@ class SketchWidget(QWidget):
painter.setPen(QPen(Qt.red, 2 / self.zoom))
painter.drawLine(p1, p2)
self.draw_measurement(painter, p1, p2)
"""for cross in self.sketch.proj_points:
self.draw_cross(painter, cross, 10 / self.zoom)
@ -825,6 +1023,9 @@ class Line2D:
# Construction Geometry
self.is_helper: bool = False
# String list with applied constraints
self.constraints: list = []
class Sketch2d(SolverSystem):
"""

33
gui.ui
View File

@ -733,6 +733,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
@ -813,6 +816,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
@ -836,9 +842,9 @@
<enum>QTabWidget::South</enum>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="">
<widget class="QWidget" name="snaps">
<attribute name="title">
<string>Setg 1</string>
</attribute>
@ -882,6 +888,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
@ -919,6 +928,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
@ -929,6 +941,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="1">
@ -949,6 +964,12 @@
<property name="text">
<string>Pnt</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
@ -959,6 +980,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1">
@ -969,6 +993,9 @@
<property name="checkable">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
@ -976,7 +1003,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="">
<widget class="QWidget" name="settings">
<attribute name="title">
<string>Setg 2</string>
</attribute>

16
license.md Normal file
View File

@ -0,0 +1,16 @@
Copyright (C) 2025 Thomas Herrmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

88
main.py
View File

@ -120,13 +120,13 @@ class MainWindow(QMainWindow):
self.ui.pb_flip_face.pressed.connect(self.on_flip_face)
###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)
self.ui.pb_linetool.clicked.connect(self.sketchWidget.act_line_mode)
self.ui.pb_con_ptpt.clicked.connect(self.sketchWidget.act_constrain_pt_pt_mode)
self.ui.pb_con_line.clicked.connect(self.sketchWidget.act_constrain_pt_line_mode)
self.ui.pb_con_horiz.clicked.connect(self.sketchWidget.act_constrain_horiz_line_mode)
self.ui.pb_con_vert.clicked.connect(self.sketchWidget.act_constrain_vert_line_mode)
self.ui.pb_con_dist.clicked.connect(self.sketchWidget.act_constrain_distance_mode)
self.ui.pb_con_mid.clicked.connect(self.sketchWidget.act_constrain_mid_point_mode)
### Operations
self.ui.pb_extrdop.pressed.connect(self.send_extrude)
@ -137,12 +137,18 @@ class MainWindow(QMainWindow):
self.setFocusPolicy(Qt.StrongFocus)
self.send_command.connect(self.custom_3D_Widget.on_receive_command)
self.ui.actionNew_Project.triggered.connect(self.new_project)
self.ui.pb_enable_construct.clicked.connect(self.sketchWidget.on_construct_change)
self.project = Project()
self.new_project()
### SNAPS
self.ui.pb_snap_midp.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("mpoint", checked))
self.ui.pb_snap_horiz.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("horiz", checked))
self.ui.pb_snap_vert.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("vert", checked))
self.ui.pb_snap_angle.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("angle", checked))
self.ui.pb_enable_snap.toggled.connect(lambda checked: self.sketchWidget.on_snap_mode_change("point", checked))
### COMPOS
### COMPOS
@ -156,7 +162,6 @@ class MainWindow(QMainWindow):
self.project.timeline = timeline
self.new_component()
def new_component(self):
print("Creating a new component...")
@ -270,7 +275,7 @@ class MainWindow(QMainWindow):
sketch.original_sketch = sketch_from_widget
#Get parameters
points = sketch_from_widget.points
points = [point for point in sketch_from_widget.points if hasattr(point, 'is_helper') and not point.is_helper]
sketch.convert_points_for_sdf(points)
sketch.id = sketch_from_widget.id
@ -280,7 +285,7 @@ class MainWindow(QMainWindow):
# Register sketch to timeline
### Add selection compo here
compo_id = self.get_activated_compo()
print("newsketch_name", sketch.id)
#print("newsketch_name", sketch.id)
self.project.timeline[compo_id].sketches[sketch.id] = sketch
# Add Item to slection menu
@ -294,6 +299,7 @@ class MainWindow(QMainWindow):
self.ui.sketch_list.setCurrentItem(items)
def on_compo_change(self):
'''This function redraws the sdf and helper mesh from available bodies and adds the names back to the list entries'''
self.custom_3D_Widget.clear_body_actors()
self.custom_3D_Widget.clear_actors_interactor()
self.custom_3D_Widget.clear_actors_projection()
@ -303,11 +309,11 @@ class MainWindow(QMainWindow):
self.ui.sketch_list.clear()
self.ui.body_list.clear()
print("id", compo_id)
print("sketch_registry", self.project.timeline[compo_id].sketches)
#print("id", compo_id)
#print("sketch_registry", self.project.timeline[compo_id].sketches)
for sketch in self.project.timeline[compo_id].sketches:
print(sketch)
#print(sketch)
self.ui.sketch_list.addItem(sketch)
for body in self.project.timeline[compo_id].bodies:
@ -353,49 +359,6 @@ class MainWindow(QMainWindow):
def on_flip_face(self):
self.send_command.emit("flip")
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)
@ -420,7 +383,7 @@ class MainWindow(QMainWindow):
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')
#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):
@ -464,8 +427,9 @@ class MainWindow(QMainWindow):
#print(sketch)
points = sketch.sdf_points
# detect loop that causes problems in mesh generation
if points[-1] == points[0]:
#detect loop that causes problems in mesh generation
print("overlap")
del points[-1]
dialog = ExtrudeDialog(self)
@ -484,8 +448,8 @@ class MainWindow(QMainWindow):
centroid = self.custom_3D_Widget.centroid
if centroid is None:
centroid = [0, 0, 0]
else:
centroid = list(centroid)
"""else:
centroid = list(centroid)"""
#print("This centroid ", centroid)
sketch.origin = centroid

View File

@ -1,66 +1,60 @@
certifi==2024.7.4
cffi==1.16.0
charset-normalizer==3.3.2
contourpy==1.2.0
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
contourpy==1.3.1
cycler==0.12.1
fonttools==4.47.0
freetype-py==2.4.0
flexcache==0.3
flexparser==0.4
fonttools==4.56.0
freetype-py==2.5.1
hsluv==5.0.4
idna==3.7
imageio==2.33.1
kiwisolver==1.4.5
lazy_loader==0.3
idna==3.10
imageio==2.37.0
kiwisolver==1.4.8
lazy_loader==0.4
markdown-it-py==3.0.0
matplotlib==3.8.2
matplotlib==3.10.1
mdurl==0.1.2
meshio==5.3.4
meshio==5.3.5
names==0.3.0
networkx==3.2.1
Nuitka==2.2.1
numpy==1.26.2
numpy-stl==3.1.1
networkx==3.4.2
Nuitka==2.6.9
numpy==2.2.4
numpy-stl==3.2.0
ordered-set==4.1.0
packaging==23.2
panda3d-gltf==1.2.0
panda3d-simplepbr==0.12.0
Pillow==10.1.0
Pint==0.22
platformdirs==4.2.2
packaging==24.2
panda3d-gltf==1.3.0
panda3d-simplepbr==0.13.0
pillow==11.1.0
Pint==0.24.4
platformdirs==4.3.7
pooch==1.8.2
pycparser==2.21
pygame==2.5.2
Pygments==2.17.2
PyOpenGL==3.1.7
pyparsing==3.1.1
PyQt6==6.7.0
PyQt6-3D==6.7.0
PyQt6-3D-Qt6==6.7.0
PyQt6-Qt6==6.7.0
PyQt6-sip==13.6.0
PySide6==6.6.1
PySide6-Addons==6.6.1
PySide6-Essentials==6.6.1
python-dateutil==2.8.2
pycparser==2.22
pygame==2.6.1
Pygments==2.19.1
pyparsing==3.2.3
PySide6_Essentials==6.8.3
python-dateutil==2.9.0.post0
python-solvespace==3.0.8
python-utils==3.8.2
pyvista==0.43.10
pyvistaqt==0.11.1
QtPy==2.4.1
python-utils==3.9.1
pyvista==0.44.2
pyvistaqt==0.11.2
QtPy==2.4.3
requests==2.32.3
rich==13.7.0
scikit-image==0.22.0
scipy==1.11.4
rich==13.9.4
scikit-image==0.25.2
scipy==1.15.2
scooby==0.10.0
sdfcad @ git+https://gitlab.com/nobodyinperson/sdfCAD@9bd4e9021c6ee7e685ee28e8a3a5d2d2c028190c
shapely==2.0.4
shiboken6==6.6.1
six==1.16.0
tifffile==2023.12.9
trimesh==4.3.2
shapely==2.1.0rc1
shiboken6==6.8.3
six==1.17.0
tifffile==2025.3.13
trimesh==4.6.5
tripy==1.0.0
typing_extensions==4.9.0
urllib3==2.2.2
vispy==0.14.2
vtk==9.3.0
vulkan==1.3.275.0
zstandard==0.22.0
typing_extensions==4.13.0
urllib3==2.3.0
vispy==0.14.3
vtk==9.4.1
vulkan==1.3.275.1
zstandard==0.23.0