- Basic oop sketch widget implement
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,3 @@
 | 
			
		||||
*.xml
 | 
			
		||||
*.iml
 | 
			
		||||
*.iml
 | 
			
		||||
.idea
 | 
			
		||||
@@ -383,8 +383,6 @@ class SketchWidget(QWidget):
 | 
			
		||||
            # Track Relationship
 | 
			
		||||
            # Points
 | 
			
		||||
 | 
			
		||||
        # CONSTRAINTS
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "pt_pt":
 | 
			
		||||
            if self.hovered_point and not self.main_buffer[0]:
 | 
			
		||||
                self.main_buffer[0] = self.get_handle_from_ui_point(self.hovered_point)
 | 
			
		||||
@@ -773,7 +771,7 @@ class SketchWidget(QWidget):
 | 
			
		||||
        return self.width() / self.height() * (1.0 / abs(self.zoom))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Point2D_ALT:
 | 
			
		||||
class Point2D:
 | 
			
		||||
    """Improved oop aaproach?"""
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.ui_point = None
 | 
			
		||||
@@ -901,35 +899,11 @@ class Point2D_ALT:
 | 
			
		||||
        return QPoint(self.to_quadrant_coords(qt_pos))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Point2D:
 | 
			
		||||
    ui_x: int = None
 | 
			
		||||
    ui_y: int = None
 | 
			
		||||
    handle = None
 | 
			
		||||
 | 
			
		||||
class Line2D:
 | 
			
		||||
    crd1: Point2D = None
 | 
			
		||||
    crd2: Point2D = None
 | 
			
		||||
    handle = None
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
class Sketch2d(SolverSystem):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.wp = self.create_2d_base()
 | 
			
		||||
        self.points = []
 | 
			
		||||
        self.lines = []
 | 
			
		||||
 | 
			
		||||
    def add_point(self, point: Point2D):
 | 
			
		||||
        point.handle = self.add_point_2d(point.ui_x, point.ui_y, self.wo)
 | 
			
		||||
        self.points.append(point)
 | 
			
		||||
 | 
			
		||||
    def add_line(self, line: Line2D):
 | 
			
		||||
        line.handle = self.add_line_2d(line.crd1, line.crd2, self.wp)
 | 
			
		||||
        self.lines.append(line)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    import sys
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										932
									
								
								drawing_modules/draw_widget_solve.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										932
									
								
								drawing_modules/draw_widget_solve.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,932 @@
 | 
			
		||||
import math
 | 
			
		||||
import re
 | 
			
		||||
from copy import copy
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
import numpy as np
 | 
			
		||||
from PySide6.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog
 | 
			
		||||
from PySide6.QtGui import QPainter, QPen, QColor, QTransform
 | 
			
		||||
from PySide6.QtCore import Qt, QPoint, QPointF, Signal, QLine
 | 
			
		||||
from python_solvespace import SolverSystem, ResultFlag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SketchWidget(QWidget):
 | 
			
		||||
    constrain_done = Signal()
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        super().__init__()
 | 
			
		||||
 | 
			
		||||
        self.line_draw_buffer = [None, None]
 | 
			
		||||
        self.drag_buffer = [None, None]
 | 
			
		||||
        self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
        self.hovered_point = None
 | 
			
		||||
        self.selected_line = None
 | 
			
		||||
 | 
			
		||||
        self.snapping_range = 20  # Range in pixels for snapping
 | 
			
		||||
        self.zoom = 1
 | 
			
		||||
 | 
			
		||||
        self.setMouseTracking(True)
 | 
			
		||||
        self.mouse_mode = False
 | 
			
		||||
        self.solv = SolverSystem()
 | 
			
		||||
 | 
			
		||||
        self.sketch = Sketch2d()
 | 
			
		||||
 | 
			
		||||
    def get_sketch(self):
 | 
			
		||||
        return self.sketch
 | 
			
		||||
 | 
			
		||||
    def reset_buffers(self):
 | 
			
		||||
        self.line_draw_buffer = [None, None]
 | 
			
		||||
        self.drag_buffer = [None, None]
 | 
			
		||||
        self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
    def set_points(self, points: list):
 | 
			
		||||
        self.points = points
 | 
			
		||||
        #self.update()
 | 
			
		||||
 | 
			
		||||
    def create_workplane(self):
 | 
			
		||||
        self.sketch.working_plane = self.solv.create_2d_base()
 | 
			
		||||
 | 
			
		||||
    def create_workplane_projected(self):
 | 
			
		||||
        self.sketch.working_plane = self.solv.create_2d_base()
 | 
			
		||||
 | 
			
		||||
    def convert_proj_points(self):
 | 
			
		||||
        out_points = []
 | 
			
		||||
        for point in self.sketch.proj_points:
 | 
			
		||||
            x, y = point
 | 
			
		||||
            coord = QPoint(x, y)
 | 
			
		||||
            out_points.append(coord)
 | 
			
		||||
 | 
			
		||||
        self.sketch.proj_points = out_points
 | 
			
		||||
 | 
			
		||||
    def convert_proj_lines(self):
 | 
			
		||||
        out_lines = []
 | 
			
		||||
        for line in self.sketch.proj_lines:
 | 
			
		||||
            start = QPoint(line[0][0], line[0][1])
 | 
			
		||||
            end = QPoint(line[1][0], line[1][1])
 | 
			
		||||
            coord = QLine(start, end)
 | 
			
		||||
            out_lines.append(coord)
 | 
			
		||||
        self.sketch.proj_lines = out_lines
 | 
			
		||||
 | 
			
		||||
    def find_duplicate_points_2d(self, edges):
 | 
			
		||||
        points = []
 | 
			
		||||
        seen = set()
 | 
			
		||||
        duplicates = []
 | 
			
		||||
 | 
			
		||||
        for edge in edges:
 | 
			
		||||
            for point in edge:
 | 
			
		||||
                # Extract only x and y coordinates
 | 
			
		||||
                point_2d = (point[0], point[1])
 | 
			
		||||
                if point_2d in seen:
 | 
			
		||||
                    if point_2d not in duplicates:
 | 
			
		||||
                        duplicates.append(point_2d)
 | 
			
		||||
                else:
 | 
			
		||||
                    seen.add(point_2d)
 | 
			
		||||
                points.append(point_2d)
 | 
			
		||||
 | 
			
		||||
        return duplicates
 | 
			
		||||
 | 
			
		||||
    def normal_to_quaternion(self, normal):
 | 
			
		||||
        normal = np.array(normal)
 | 
			
		||||
        #normal = normal / np.linalg.norm(normal)
 | 
			
		||||
 | 
			
		||||
        axis = np.cross([0, 0, 1], normal)
 | 
			
		||||
        if np.allclose(axis, 0):
 | 
			
		||||
            axis = np.array([1, 0, 0])
 | 
			
		||||
        else:
 | 
			
		||||
            axis = axis / np.linalg.norm(axis)  # Normalize the axis
 | 
			
		||||
 | 
			
		||||
        angle = np.arccos(np.dot([0, 0, 1], normal))
 | 
			
		||||
 | 
			
		||||
        qw = np.cos(angle / 2)
 | 
			
		||||
        sin_half_angle = np.sin(angle / 2)
 | 
			
		||||
        qx, qy, qz = axis * sin_half_angle  # This will now work correctly
 | 
			
		||||
 | 
			
		||||
        return qw, qx, qy, qz
 | 
			
		||||
 | 
			
		||||
    def create_workplane_space(self, points, normal):
 | 
			
		||||
        print("edges", points)
 | 
			
		||||
        origin = self.find_duplicate_points_2d(points)
 | 
			
		||||
        print(origin)
 | 
			
		||||
        x, y = origin[0]
 | 
			
		||||
        origin = QPoint(x, y)
 | 
			
		||||
 | 
			
		||||
        origin_handle = self.get_handle_from_ui_point(origin)
 | 
			
		||||
        qw, qx, qy, qz = self.normal_to_quaternion(normal)
 | 
			
		||||
 | 
			
		||||
        slv_normal = self.solv.add_normal_3d(qw, qx, qy, qz)
 | 
			
		||||
        self.sketch.working_plane = self.solv.add_work_plane(origin_handle, slv_normal)
 | 
			
		||||
        print(self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
    def get_handle_nr(self, input_str: str) -> int:
 | 
			
		||||
        # Define the regex pattern to extract the handle number
 | 
			
		||||
        pattern = r"handle=(\d+)"
 | 
			
		||||
 | 
			
		||||
        # Use re.search to find the handle number in the string
 | 
			
		||||
        match = re.search(pattern, input_str)
 | 
			
		||||
 | 
			
		||||
        if match:
 | 
			
		||||
            handle_number = int(match.group(1))
 | 
			
		||||
            print(f"Handle number: {handle_number}")
 | 
			
		||||
            return int(handle_number)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            print("Handle number not found.")
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
    def get_keys(self, d: dict, target: QPoint) -> list:
 | 
			
		||||
        result = []
 | 
			
		||||
        path = []
 | 
			
		||||
        print(d)
 | 
			
		||||
        print(target)
 | 
			
		||||
        for k, v in d.items():
 | 
			
		||||
            path.append(k)
 | 
			
		||||
            if isinstance(v, dict):
 | 
			
		||||
                self.get_keys(v, target)
 | 
			
		||||
            if v == target:
 | 
			
		||||
                result.append(copy(path))
 | 
			
		||||
            path.pop()
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def get_handle_from_ui_point(self, ui_point: QPoint):
 | 
			
		||||
        """Input QPoint and you shall reveive a slvs entity handle!"""
 | 
			
		||||
        for point in self.sketch.points:
 | 
			
		||||
            if ui_point == point.ui_point:
 | 
			
		||||
                slv_handle = point.handle
 | 
			
		||||
 | 
			
		||||
                return slv_handle
 | 
			
		||||
 | 
			
		||||
    def get_line_handle_from_ui_point(self, ui_point: QPoint):
 | 
			
		||||
        """Input Qpoint that is on a line and you shall receive the handle of the line!"""
 | 
			
		||||
        for target_line_con in self.sketch.lines:
 | 
			
		||||
            if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
 | 
			
		||||
                slv_handle = target_line_con['solv_handle']
 | 
			
		||||
 | 
			
		||||
                return slv_handle
 | 
			
		||||
 | 
			
		||||
    def get_point_line_handles_from_ui_point(self, ui_point: QPoint) -> tuple:
 | 
			
		||||
        """Input Qpoint that is on a line and you shall receive the handles of the points of the line!"""
 | 
			
		||||
        for target_line_con in self.sketch.slv_lines:
 | 
			
		||||
            if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
 | 
			
		||||
                lines_to_cons = target_line_con['solv_entity_points']
 | 
			
		||||
 | 
			
		||||
                return lines_to_cons
 | 
			
		||||
 | 
			
		||||
    def distance(self, p1, p2):
 | 
			
		||||
        return math.sqrt((p1.x() - p2.x())**2 + (p1.y() - p2.y())**2)
 | 
			
		||||
 | 
			
		||||
    def calculate_midpoint(self, point1, point2):
 | 
			
		||||
        mx = (point1.x() + point2.x()) // 2
 | 
			
		||||
        my = (point1.y() + point2.y()) // 2
 | 
			
		||||
        return QPoint(mx, my)
 | 
			
		||||
 | 
			
		||||
    def is_point_on_line(self, p, p1, p2, tolerance=5):
 | 
			
		||||
        # Calculate the lengths of the sides of the triangle
 | 
			
		||||
        a = self.distance(p, p1)
 | 
			
		||||
        b = self.distance(p, p2)
 | 
			
		||||
        c = self.distance(p1, p2)
 | 
			
		||||
 | 
			
		||||
        # Calculate the semi-perimeter
 | 
			
		||||
        s = (a + b + c) / 2
 | 
			
		||||
 | 
			
		||||
        # Calculate the area using Heron's formula
 | 
			
		||||
        area = math.sqrt(s * (s - a) * (s - b) * (s - c))
 | 
			
		||||
 | 
			
		||||
        # Calculate the height (perpendicular distance from the point to the line)
 | 
			
		||||
        if c > 0:
 | 
			
		||||
            height = (2 * area) / c
 | 
			
		||||
            # Check if the height is within the tolerance distance to the line
 | 
			
		||||
            if height > tolerance:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            # Check if the projection of the point onto the line is within the line segment
 | 
			
		||||
            dot_product = ((p.x() - p1.x()) * (p2.x() - p1.x()) + (p.y() - p1.y()) * (p2.y() - p1.y())) / (c ** 2)
 | 
			
		||||
 | 
			
		||||
            return 0 <= dot_product <= 1
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
 | 
			
		||||
        return QPoint(self.to_quadrant_coords(qt_pos))
 | 
			
		||||
 | 
			
		||||
    def check_all_points(self,) -> list:
 | 
			
		||||
        old_points_ui = []
 | 
			
		||||
        new_points_ui = []
 | 
			
		||||
 | 
			
		||||
        for old_point_ui in self.sketch.points:
 | 
			
		||||
            old_points_ui.append(old_point_ui.ui_point)
 | 
			
		||||
 | 
			
		||||
        for i in range(self.solv.entity_len()):
 | 
			
		||||
            # Iterate though full length because mixed list from SS
 | 
			
		||||
            entity = self.solv.entity(i)
 | 
			
		||||
            if entity.is_point_2d() and self.solv.params(entity.params):
 | 
			
		||||
                x_tbu, y_tbu = self.solv.params(entity.params)
 | 
			
		||||
                point_solved = QPoint(x_tbu, y_tbu)
 | 
			
		||||
                new_points_ui.append(point_solved)
 | 
			
		||||
 | 
			
		||||
        # Now we have old_points_ui and new_points_ui, let's compare them
 | 
			
		||||
        differences = []
 | 
			
		||||
 | 
			
		||||
        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)):
 | 
			
		||||
            if old_point != new_point:
 | 
			
		||||
                differences.append((index, old_point, new_point))
 | 
			
		||||
 | 
			
		||||
        return differences
 | 
			
		||||
 | 
			
		||||
    def update_ui_points(self, point_list: list):
 | 
			
		||||
        # Print initial state of slv_points_main
 | 
			
		||||
        # print("Initial slv_points_main:", self.slv_points_main)
 | 
			
		||||
        print("Change list:", point_list)
 | 
			
		||||
 | 
			
		||||
        if len(point_list) > 0:
 | 
			
		||||
            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].point = new_point
 | 
			
		||||
            # Print updated state
 | 
			
		||||
            # print("Updated slv_points_main:", self.slv_points_main)
 | 
			
		||||
 | 
			
		||||
    def check_all_lines_and_update(self,changed_points: list):
 | 
			
		||||
        for tbu_points_idx in changed_points:
 | 
			
		||||
            index, old_point, new_point = tbu_points_idx
 | 
			
		||||
            for line_needs_update in self.sketch.lines:
 | 
			
		||||
                if old_point == line_needs_update.points[0]:
 | 
			
		||||
                    line_needs_update['ui_points'][0] = new_point
 | 
			
		||||
                elif old_point == line_needs_update.points[1]:
 | 
			
		||||
                    line_needs_update['ui_points'][1] = new_point
 | 
			
		||||
 | 
			
		||||
    def mouseReleaseEvent(self, event):
 | 
			
		||||
        local_event_pos = self.viewport_to_local_coord(event.pos())
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and not self.mouse_mode:
 | 
			
		||||
            self.drag_buffer[1] = local_event_pos
 | 
			
		||||
 | 
			
		||||
            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]
 | 
			
		||||
                new_params = self.drag_buffer[1].x(), self.drag_buffer[1].y()
 | 
			
		||||
                self.sketch.set_params(entry.params, new_params)
 | 
			
		||||
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
                self.update()
 | 
			
		||||
                self.drag_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
    def mousePressEvent(self, event):
 | 
			
		||||
        local_event_pos = self.viewport_to_local_coord(event.pos())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and not self.mouse_mode:
 | 
			
		||||
            self.drag_buffer[0] = self.get_handle_from_ui_point(self.hovered_point)
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.RightButton and self.mouse_mode:
 | 
			
		||||
            self.reset_buffers()
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "line":
 | 
			
		||||
            if self.hovered_point:
 | 
			
		||||
                clicked_pos = self.hovered_point
 | 
			
		||||
            else:
 | 
			
		||||
                clicked_pos = local_event_pos
 | 
			
		||||
 | 
			
		||||
            if not self.line_draw_buffer[0]:
 | 
			
		||||
 | 
			
		||||
                u = clicked_pos.x()
 | 
			
		||||
                v = clicked_pos.y()
 | 
			
		||||
 | 
			
		||||
                point = Point2D(u,v)
 | 
			
		||||
                self.sketch.add_point(point)
 | 
			
		||||
 | 
			
		||||
                self.line_draw_buffer[0] = point
 | 
			
		||||
 | 
			
		||||
            elif self.line_draw_buffer[0]:
 | 
			
		||||
 | 
			
		||||
                u = clicked_pos.x()
 | 
			
		||||
                v = clicked_pos.y()
 | 
			
		||||
 | 
			
		||||
                point = Point2D(u, v)
 | 
			
		||||
                self.sketch.add_point(point)
 | 
			
		||||
 | 
			
		||||
                self.line_draw_buffer[1] = point
 | 
			
		||||
 | 
			
		||||
            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])
 | 
			
		||||
                self.sketch.add_line(line)
 | 
			
		||||
 | 
			
		||||
                # Reset the buffer for the next line segment
 | 
			
		||||
                self.line_draw_buffer[0] = self.line_draw_buffer[1]
 | 
			
		||||
                self.line_draw_buffer[1] = None
 | 
			
		||||
 | 
			
		||||
            # Track Relationship
 | 
			
		||||
            # Points
 | 
			
		||||
 | 
			
		||||
        # CONSTRAINTS
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "pt_pt":
 | 
			
		||||
            if self.hovered_point and not self.main_buffer[0]:
 | 
			
		||||
                self.main_buffer[0] = self.get_handle_from_ui_point(self.hovered_point)
 | 
			
		||||
 | 
			
		||||
            elif self.main_buffer[0]:
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
                self.solv.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
                if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                    print("Fuck yeah")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                    print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                    print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                    print("Solve_failed - Incons")
 | 
			
		||||
                self.constrain_done.emit()
 | 
			
		||||
                self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "pt_line":
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
            elif self.main_buffer[0]:
 | 
			
		||||
                self.main_buffer[1] = self.get_line_handle_from_ui_point(local_event_pos)
 | 
			
		||||
 | 
			
		||||
                # Contrain point to line
 | 
			
		||||
                if self.main_buffer[1]:
 | 
			
		||||
                    self.solv.coincident(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
                    if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                        print("Fuck yeah")
 | 
			
		||||
                        self.constrain_done.emit()
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                        print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                        print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                        print("Solve_failed - Incons")
 | 
			
		||||
 | 
			
		||||
                    self.constrain_done.emit()
 | 
			
		||||
                    # Clear saved_points after solve attempt
 | 
			
		||||
                    self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "pb_con_mid":
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
            elif self.main_buffer[0]:
 | 
			
		||||
                self.main_buffer[1] = self.get_line_handle_from_ui_point(local_event_pos)
 | 
			
		||||
 | 
			
		||||
                # Contrain point to line
 | 
			
		||||
                if self.main_buffer[1]:
 | 
			
		||||
                    self.solv.midpoint(self.main_buffer[0], self.main_buffer[1], self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
                    if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                        print("Fuck yeah")
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                        print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                        print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
                    elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                        print("Solve_failed - Incons")
 | 
			
		||||
                    self.constrain_done.emit()
 | 
			
		||||
 | 
			
		||||
                    self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "horiz":
 | 
			
		||||
 | 
			
		||||
            line_selected = self.get_line_handle_from_ui_point(local_event_pos)
 | 
			
		||||
 | 
			
		||||
            if line_selected:
 | 
			
		||||
                self.solv.horizontal(line_selected, self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
            if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                print("Fuck yeah")
 | 
			
		||||
 | 
			
		||||
            elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
            elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
            elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                print("Solve_failed - Incons")
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "vert":
 | 
			
		||||
            line_selected = self.get_line_handle_from_ui_point(local_event_pos)
 | 
			
		||||
 | 
			
		||||
            if line_selected:
 | 
			
		||||
                self.solv.vertical(line_selected, self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
                if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                    print("Fuck yeah")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                    print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                    print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                    print("Solve_failed - Incons")
 | 
			
		||||
 | 
			
		||||
        if event.button() == Qt.LeftButton and self.mouse_mode == "distance":
 | 
			
		||||
            # Depending on selected elemnts either point line or line distance
 | 
			
		||||
            #print("distance")
 | 
			
		||||
            e1 = None
 | 
			
		||||
            e2 = None
 | 
			
		||||
 | 
			
		||||
            if self.hovered_point:
 | 
			
		||||
                print("buf point")
 | 
			
		||||
                # Get the point as UI point as buffer
 | 
			
		||||
                self.main_buffer[0] = self.hovered_point
 | 
			
		||||
 | 
			
		||||
            elif self.selected_line:
 | 
			
		||||
                # Get the point as UI point as buffer
 | 
			
		||||
                self.main_buffer[1] = local_event_pos
 | 
			
		||||
 | 
			
		||||
            if self.main_buffer[0] and self.main_buffer[1]:
 | 
			
		||||
                # Define point line combination
 | 
			
		||||
                e1 = self.get_handle_from_ui_point(self.main_buffer[0])
 | 
			
		||||
                e2 = self.get_line_handle_from_ui_point(self.main_buffer[1])
 | 
			
		||||
 | 
			
		||||
            elif not self.main_buffer[0]:
 | 
			
		||||
                # Define only line selection
 | 
			
		||||
                e1, e2 = self.get_point_line_handles_from_ui_point(local_event_pos)
 | 
			
		||||
 | 
			
		||||
            if e1 and e2:
 | 
			
		||||
                # Ask fo the dimension and solve if both elements are present
 | 
			
		||||
                length, ok = QInputDialog.getDouble(self, 'Distance', 'Enter a mm value:', value=100, decimals=2)
 | 
			
		||||
                self.solv.distance(e1, e2, length, self.sketch.working_plane)
 | 
			
		||||
 | 
			
		||||
                if self.solv.solve() == ResultFlag.OKAY:
 | 
			
		||||
                    print("Fuck yeah")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.DIDNT_CONVERGE:
 | 
			
		||||
                    print("Solve_failed - Converge")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.TOO_MANY_UNKNOWNS:
 | 
			
		||||
                    print("Solve_failed - Unknowns")
 | 
			
		||||
 | 
			
		||||
                elif self.solv.solve() == ResultFlag.INCONSISTENT:
 | 
			
		||||
                    print("Solve_failed - Incons")
 | 
			
		||||
 | 
			
		||||
                self.constrain_done.emit()
 | 
			
		||||
                self.main_buffer = [None, None]
 | 
			
		||||
 | 
			
		||||
        # Update the main point list with the new elements and draw them
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
    def mouseMoveEvent(self, event):
 | 
			
		||||
        local_event_pos = self.viewport_to_local_coord(event.pos())
 | 
			
		||||
 | 
			
		||||
        closest_point = None
 | 
			
		||||
        min_distance = float('inf')
 | 
			
		||||
        threshold = 10  # Distance threshold for highlighting
 | 
			
		||||
 | 
			
		||||
        if len(self.sketch.points) > 0:
 | 
			
		||||
 | 
			
		||||
            for point in self.sketch.points:
 | 
			
		||||
                distance = (local_event_pos - point.ui_point).manhattanLength()
 | 
			
		||||
                if distance < threshold and distance < min_distance:
 | 
			
		||||
                    closest_point = point.ui_point
 | 
			
		||||
                    min_distance = distance
 | 
			
		||||
 | 
			
		||||
            """for point in self.sketch.proj_points:
 | 
			
		||||
                distance = (local_event_pos - point).manhattanLength()
 | 
			
		||||
                if distance < threshold and distance < min_distance:
 | 
			
		||||
                    closest_point = point
 | 
			
		||||
                    min_distance = distance"""
 | 
			
		||||
 | 
			
		||||
            if closest_point != self.hovered_point:
 | 
			
		||||
                self.hovered_point = closest_point
 | 
			
		||||
                print(self.hovered_point)
 | 
			
		||||
 | 
			
		||||
            for line in self.sketch.lines:
 | 
			
		||||
                p1 = line.crd1.ui_point
 | 
			
		||||
                p2 = line.crd2.ui_point
 | 
			
		||||
 | 
			
		||||
                if self.is_point_on_line(local_event_pos, p1, p2):
 | 
			
		||||
                    self.selected_line = p1, p2
 | 
			
		||||
                    break
 | 
			
		||||
                else:
 | 
			
		||||
                    self.selected_line = None
 | 
			
		||||
 | 
			
		||||
            self.update()
 | 
			
		||||
 | 
			
		||||
    def mouseDoubleClickEvent(self, event):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def drawBackgroundGrid(self, painter):
 | 
			
		||||
        """Draw a background grid."""
 | 
			
		||||
        grid_spacing = 50
 | 
			
		||||
        pen = QPen(QColor(200, 200, 200), 1, Qt.SolidLine)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
        # Draw vertical grid lines
 | 
			
		||||
        for x in range(-self.width() // 2, self.width() // 2, grid_spacing):
 | 
			
		||||
            painter.drawLine(x, -self.height() // 2, x, self.height() // 2)
 | 
			
		||||
 | 
			
		||||
        # Draw horizontal grid lines
 | 
			
		||||
        for y in range(-self.height() // 2, self.height() // 2, grid_spacing):
 | 
			
		||||
            painter.drawLine(-self.width() // 2, y, self.width() // 2, y)
 | 
			
		||||
 | 
			
		||||
    def drawAxes(self, painter):
 | 
			
		||||
        painter.setRenderHint(QPainter.Antialiasing)
 | 
			
		||||
 | 
			
		||||
        # Set up pen for dashed lines
 | 
			
		||||
        pen = QPen(Qt.gray, 1, Qt.DashLine)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
        middle_x = self.width() // 2
 | 
			
		||||
        middle_y = self.height() // 2
 | 
			
		||||
 | 
			
		||||
        # Draw X axis as dashed line
 | 
			
		||||
        painter.drawLine(0, middle_y, self.width(), middle_y)
 | 
			
		||||
 | 
			
		||||
        # Draw Y axis as dashed line
 | 
			
		||||
        painter.drawLine(middle_x, 0, middle_x, self.height())
 | 
			
		||||
 | 
			
		||||
        # Draw tick marks
 | 
			
		||||
        tick_length = int(10  * self.zoom)
 | 
			
		||||
        tick_spacing = int(50 * self.zoom)
 | 
			
		||||
 | 
			
		||||
        pen = QPen(Qt.gray, 1, Qt.SolidLine)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
        # Draw tick marks on the X axis to the right and left from the middle point
 | 
			
		||||
        for x in range(0, self.width() // 2, tick_spacing):
 | 
			
		||||
            painter.drawLine(middle_x + x, middle_y - tick_length // 2, middle_x + x, middle_y + tick_length // 2)
 | 
			
		||||
            painter.drawLine(middle_x - x, middle_y - tick_length // 2, middle_x - x, middle_y + tick_length // 2)
 | 
			
		||||
 | 
			
		||||
        # Draw tick marks on the Y axis upwards and downwards from the middle point
 | 
			
		||||
        for y in range(0, self.height() // 2, tick_spacing):
 | 
			
		||||
            painter.drawLine(middle_x - tick_length // 2, middle_y + y, middle_x + tick_length // 2, middle_y + y)
 | 
			
		||||
            painter.drawLine(middle_x - tick_length // 2, middle_y - y, middle_x + tick_length // 2, middle_y - y)
 | 
			
		||||
 | 
			
		||||
        # Draw the origin point in red
 | 
			
		||||
        painter.setPen(QPen(Qt.red, 4))
 | 
			
		||||
        painter.drawPoint(middle_x, middle_y)
 | 
			
		||||
 | 
			
		||||
    def draw_cross(self, painter, pos: QPoint, size=10):
 | 
			
		||||
        # Set up the pen
 | 
			
		||||
        pen = QPen(QColor('green'))  # You can change the color as needed
 | 
			
		||||
        pen.setWidth(int(2 / self.zoom))  # Set the line widt)h
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
        x = pos.x()
 | 
			
		||||
        y = pos.y()
 | 
			
		||||
 | 
			
		||||
        # 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
 | 
			
		||||
        center_y = self.height() // 2
 | 
			
		||||
        quadrant_x = point.x() - center_x
 | 
			
		||||
        quadrant_y = center_y - point.y()  # Note the change here
 | 
			
		||||
        return QPoint(quadrant_x, quadrant_y) / self.zoom
 | 
			
		||||
 | 
			
		||||
    def from_quadrant_coords(self, point: QPoint):
 | 
			
		||||
        """Translate quadrant coordinates to linear coordinates."""
 | 
			
		||||
        center_x = self.width() // 2
 | 
			
		||||
        center_y = self.height() // 2
 | 
			
		||||
        widget_x = center_x + point.x() * self.zoom
 | 
			
		||||
        widget_y = center_y - point.y() * self.zoom  # Note the subtraction here
 | 
			
		||||
        
 | 
			
		||||
        return QPoint(int(widget_x), int(widget_y))
 | 
			
		||||
 | 
			
		||||
    def from_quadrant_coords_no_center(self, point):
 | 
			
		||||
        """Invert Y Coordinate for mesh"""
 | 
			
		||||
        center_x = 0
 | 
			
		||||
        center_y = 0
 | 
			
		||||
        widget_x = point.x()
 | 
			
		||||
        widget_y = -point.y()
 | 
			
		||||
        return QPoint(int(widget_x), int(widget_y))
 | 
			
		||||
 | 
			
		||||
    def paintEvent(self, event):
 | 
			
		||||
        painter = QPainter(self)
 | 
			
		||||
        painter.setRenderHint(QPainter.Antialiasing)
 | 
			
		||||
 | 
			
		||||
        self.drawAxes(painter)
 | 
			
		||||
 | 
			
		||||
        # Create a QTransform object
 | 
			
		||||
        transform = QTransform()
 | 
			
		||||
 | 
			
		||||
        # Translate the origin to the center of the widget
 | 
			
		||||
        center = QPointF(self.width() / 2, self.height() / 2)
 | 
			
		||||
        transform.translate(center.x(), center.y())
 | 
			
		||||
 | 
			
		||||
        # Apply the zoom factor
 | 
			
		||||
        transform.scale(self.zoom, -self.zoom)  # Negative y-scale to invert y-axis
 | 
			
		||||
 | 
			
		||||
        # Set the transform to the painter
 | 
			
		||||
        painter.setTransform(transform)
 | 
			
		||||
 | 
			
		||||
        pen = QPen(Qt.gray)
 | 
			
		||||
        pen.setWidthF(2 / self.zoom)
 | 
			
		||||
        painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
        # Draw points
 | 
			
		||||
        if self.sketch:
 | 
			
		||||
            for point in self.sketch.points:
 | 
			
		||||
                painter.drawEllipse(point.ui_point, 3 / self.zoom, 3 / self.zoom)
 | 
			
		||||
 | 
			
		||||
            for line in self.sketch.lines:
 | 
			
		||||
                p1 = line.crd1.ui_point
 | 
			
		||||
                p2 = line.crd2.ui_point
 | 
			
		||||
                painter.drawLine(p1, p2)
 | 
			
		||||
 | 
			
		||||
                dis = self.distance(p1, p2)
 | 
			
		||||
                mid = self.calculate_midpoint(p1, p2)
 | 
			
		||||
                painter.drawText(mid, str(round(dis, 2)))
 | 
			
		||||
 | 
			
		||||
            pen = QPen(Qt.green)
 | 
			
		||||
            pen.setWidthF(2 / self.zoom)
 | 
			
		||||
            painter.setPen(pen)
 | 
			
		||||
 | 
			
		||||
            if self.solv.entity_len():
 | 
			
		||||
                for i in range(self.solv.entity_len()):
 | 
			
		||||
                    entity = self.solv.entity(i)
 | 
			
		||||
                    if entity.is_point_2d() and self.solv.params(entity.params):
 | 
			
		||||
                        x, y = self.solv.params(entity.params)
 | 
			
		||||
                        point = QPointF(x, y)
 | 
			
		||||
                        painter.drawEllipse(point, 6 / self.zoom, 6 / self.zoom)
 | 
			
		||||
 | 
			
		||||
            # Highlight point hovered
 | 
			
		||||
            if self.hovered_point:
 | 
			
		||||
                highlight_pen = QPen(QColor(255, 0, 0))
 | 
			
		||||
                highlight_pen.setWidthF(2 / self.zoom)
 | 
			
		||||
                painter.setPen(highlight_pen)
 | 
			
		||||
                painter.drawEllipse(self.hovered_point, 5 / self.zoom, 5 / self.zoom)
 | 
			
		||||
 | 
			
		||||
            # Highlight line hovered
 | 
			
		||||
            if self.selected_line and not self.hovered_point:
 | 
			
		||||
                p1, p2 = self.selected_line
 | 
			
		||||
                painter.setPen(QPen(Qt.red, 2 / self.zoom))
 | 
			
		||||
                painter.drawLine(p1, p2)
 | 
			
		||||
 | 
			
		||||
            """for cross in self.sketch.proj_points:
 | 
			
		||||
                self.draw_cross(painter, cross, 10 / self.zoom)
 | 
			
		||||
 | 
			
		||||
            for selected in self.sketch.proj_lines:
 | 
			
		||||
                pen = QPen(Qt.white, 1, Qt.DashLine)
 | 
			
		||||
                painter.setPen(pen)
 | 
			
		||||
                painter.drawLine(selected)"""
 | 
			
		||||
 | 
			
		||||
        painter.end()
 | 
			
		||||
 | 
			
		||||
    def wheelEvent(self, event):
 | 
			
		||||
        delta = event.angleDelta().y()
 | 
			
		||||
        self.zoom += (delta / 200) * 0.1
 | 
			
		||||
        self.update()
 | 
			
		||||
    
 | 
			
		||||
    def aspect_ratio(self):
 | 
			
		||||
        return self.width() / self.height() * (1.0 / abs(self.zoom))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Point2D_ALT:
 | 
			
		||||
    """Improved oop aaproach?"""
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.ui_point = None
 | 
			
		||||
        self.solve_handle_nr = None
 | 
			
		||||
        self.solve_handle = None
 | 
			
		||||
        self.part_of_entity = None
 | 
			
		||||
 | 
			
		||||
    def to_quadrant_coords(self, point):
 | 
			
		||||
        """Translate linear coordinates to quadrant coordinates."""
 | 
			
		||||
        center_x = self.width() // 2
 | 
			
		||||
        center_y = self.height() // 2
 | 
			
		||||
        quadrant_x = point.x() - center_x
 | 
			
		||||
        quadrant_y = center_y - point.y()  # Note the change here
 | 
			
		||||
 | 
			
		||||
        return QPoint(quadrant_x, quadrant_y) / self.zoom
 | 
			
		||||
 | 
			
		||||
    def from_quadrant_coords(self, point: QPoint):
 | 
			
		||||
        """Translate quadrant coordinates to linear coordinates."""
 | 
			
		||||
        center_x = self.width() // 2
 | 
			
		||||
        center_y = self.height() // 2
 | 
			
		||||
        widget_x = center_x + point.x() * self.zoom
 | 
			
		||||
        widget_y = center_y - point.y() * self.zoom  # Note the subtraction here
 | 
			
		||||
 | 
			
		||||
        return QPoint(int(widget_x), int(widget_y))
 | 
			
		||||
 | 
			
		||||
    def from_quadrant_coords_no_center(self, point):
 | 
			
		||||
        """Invert Y Coordinate for mesh"""
 | 
			
		||||
        center_x = 0
 | 
			
		||||
        center_y = 0
 | 
			
		||||
        widget_x = point.x()
 | 
			
		||||
        widget_y = -point.y()
 | 
			
		||||
 | 
			
		||||
        return QPoint(int(widget_x), int(widget_y))
 | 
			
		||||
 | 
			
		||||
    def get_handle_nr(self, input_str: str) -> int:
 | 
			
		||||
        # Define the regex pattern to extract the handle number
 | 
			
		||||
        pattern = r"handle=(\d+)"
 | 
			
		||||
 | 
			
		||||
        # Use re.search to find the handle number in the string
 | 
			
		||||
        match = re.search(pattern, input_str)
 | 
			
		||||
 | 
			
		||||
        if match:
 | 
			
		||||
            handle_number = int(match.group(1))
 | 
			
		||||
            print(f"Handle number: {handle_number}")
 | 
			
		||||
            return int(handle_number)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            print("Handle number not found.")
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
    def get_keys(self, d: dict, target: QPoint) -> list:
 | 
			
		||||
        result = []
 | 
			
		||||
        path = []
 | 
			
		||||
        print(d)
 | 
			
		||||
        print(target)
 | 
			
		||||
        for k, v in d.items():
 | 
			
		||||
            path.append(k)
 | 
			
		||||
            if isinstance(v, dict):
 | 
			
		||||
                self.get_keys(v, target)
 | 
			
		||||
            if v == target:
 | 
			
		||||
                result.append(copy(path))
 | 
			
		||||
            path.pop()
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def get_handle_from_ui_point(self, ui_point: QPoint):
 | 
			
		||||
        """Input QPoint and you shall reveive a slvs entity handle!"""
 | 
			
		||||
        for point in self.sketch.slv_points:
 | 
			
		||||
            if ui_point == point['ui_point']:
 | 
			
		||||
                slv_handle = point['solv_handle']
 | 
			
		||||
 | 
			
		||||
                return slv_handle
 | 
			
		||||
 | 
			
		||||
    def get_line_handle_from_ui_point(self, ui_point: QPoint):
 | 
			
		||||
        """Input Qpoint that is on a line and you shall receive the handle of the line!"""
 | 
			
		||||
        for target_line_con in self.sketch.slv_lines:
 | 
			
		||||
            if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
 | 
			
		||||
                slv_handle = target_line_con['solv_handle']
 | 
			
		||||
 | 
			
		||||
                return slv_handle
 | 
			
		||||
 | 
			
		||||
    def get_point_line_handles_from_ui_point(self, ui_point: QPoint) -> tuple:
 | 
			
		||||
        """Input Qpoint that is on a line and you shall receive the handles of the points of the line!"""
 | 
			
		||||
        for target_line_con in self.sketch.slv_lines:
 | 
			
		||||
            if self.is_point_on_line(ui_point, target_line_con['ui_points'][0], target_line_con['ui_points'][1]):
 | 
			
		||||
                lines_to_cons = target_line_con['solv_entity_points']
 | 
			
		||||
 | 
			
		||||
                return lines_to_cons
 | 
			
		||||
 | 
			
		||||
    def distance(self, p1, p2):
 | 
			
		||||
        return math.sqrt((p1.x() - p2.x())**2 + (p1.y() - p2.y())**2)
 | 
			
		||||
 | 
			
		||||
    def calculate_midpoint(self, point1, point2):
 | 
			
		||||
        mx = (point1.x() + point2.x()) // 2
 | 
			
		||||
        my = (point1.y() + point2.y()) // 2
 | 
			
		||||
        return QPoint(mx, my)
 | 
			
		||||
 | 
			
		||||
    def is_point_on_line(self, p, p1, p2, tolerance=5):
 | 
			
		||||
        # Calculate the lengths of the sides of the triangle
 | 
			
		||||
        a = self.distance(p, p1)
 | 
			
		||||
        b = self.distance(p, p2)
 | 
			
		||||
        c = self.distance(p1, p2)
 | 
			
		||||
 | 
			
		||||
        # Calculate the semi-perimeter
 | 
			
		||||
        s = (a + b + c) / 2
 | 
			
		||||
 | 
			
		||||
        # Calculate the area using Heron's formula
 | 
			
		||||
        area = math.sqrt(s * (s - a) * (s - b) * (s - c))
 | 
			
		||||
 | 
			
		||||
        # Calculate the height (perpendicular distance from the point to the line)
 | 
			
		||||
        if c > 0:
 | 
			
		||||
            height = (2 * area) / c
 | 
			
		||||
            # Check if the height is within the tolerance distance to the line
 | 
			
		||||
            if height > tolerance:
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
            # Check if the projection of the point onto the line is within the line segment
 | 
			
		||||
            dot_product = ((p.x() - p1.x()) * (p2.x() - p1.x()) + (p.y() - p1.y()) * (p2.y() - p1.y())) / (c ** 2)
 | 
			
		||||
 | 
			
		||||
            return 0 <= dot_product <= 1
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def viewport_to_local_coord(self, qt_pos : QPoint) -> QPoint:
 | 
			
		||||
        return QPoint(self.to_quadrant_coords(qt_pos))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Point2D:
 | 
			
		||||
    def __init__(self, x, y):
 | 
			
		||||
        self.ui_x: int = x
 | 
			
		||||
        self.ui_y: int = y
 | 
			
		||||
        self.ui_point = QPoint(self.ui_x, self.ui_y)
 | 
			
		||||
        self.handle = None
 | 
			
		||||
        self.handle_nr: int = None
 | 
			
		||||
 | 
			
		||||
class Line2D:
 | 
			
		||||
    def __init__(self, point_s: Point2D, point_e: Point2D):
 | 
			
		||||
        self.crd1: Point2D = point_s
 | 
			
		||||
        self.crd2: Point2D = point_e
 | 
			
		||||
        self.handle = None
 | 
			
		||||
        self.handle_nr = None
 | 
			
		||||
 | 
			
		||||
class Sketch2d(SolverSystem):
 | 
			
		||||
    """
 | 
			
		||||
    Primary class for internal drawing based on the SolveSpace libray
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.wp = self.create_2d_base()
 | 
			
		||||
        self.points = []
 | 
			
		||||
        self.lines = []
 | 
			
		||||
 | 
			
		||||
    def add_point(self, point: Point2D):
 | 
			
		||||
        """
 | 
			
		||||
        Adds a point into the solversystem and reurns the handle.
 | 
			
		||||
        Appends the added point to the points list.
 | 
			
		||||
        :param point: 2D point in Point2D class format
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        point.handle = self.add_point_2d(point.ui_x, point.ui_y, self.wp)
 | 
			
		||||
        point.handle_nr = self.get_handle_nr(str(point.handle))
 | 
			
		||||
 | 
			
		||||
        self.points.append(point)
 | 
			
		||||
 | 
			
		||||
    def add_line(self, line: Line2D):
 | 
			
		||||
        """
 | 
			
		||||
        Adds a line into the solversystem and returns the handle.
 | 
			
		||||
        Appends the added line to the line list.
 | 
			
		||||
        :param line:
 | 
			
		||||
        :param point: 2D point in Point2D class format
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        line.handle = self.add_line_2d(line.crd1.handle, line.crd2.handle, self.wp)
 | 
			
		||||
        line.handle_nr = self.get_handle_nr(str(line.handle))
 | 
			
		||||
 | 
			
		||||
        self.lines.append(line)
 | 
			
		||||
 | 
			
		||||
    ### HELPER AND TOOLS
 | 
			
		||||
    def get_handle_nr(self, input_str: str) -> int:
 | 
			
		||||
        # Define the regex pattern to extract the handle number
 | 
			
		||||
        pattern = r"handle=(\d+)"
 | 
			
		||||
 | 
			
		||||
        # Use re.search to find the handle number in the string
 | 
			
		||||
        match = re.search(pattern, input_str)
 | 
			
		||||
 | 
			
		||||
        if match:
 | 
			
		||||
            handle_number = int(match.group(1))
 | 
			
		||||
            print(f"Handle number: {handle_number}")
 | 
			
		||||
            return int(handle_number)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            print("Handle number not found.")
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    import sys
 | 
			
		||||
 | 
			
		||||
    app = QApplication(sys.argv)
 | 
			
		||||
    window = SketchWidget()
 | 
			
		||||
    window.setWindowTitle("Snap Line Widget")
 | 
			
		||||
    window.resize(800, 600)
 | 
			
		||||
    window.show()
 | 
			
		||||
    sys.exit(app.exec())
 | 
			
		||||
							
								
								
									
										5
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								main.py
									
									
									
									
									
								
							@@ -10,7 +10,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow, QSizePolicy, QInputDial
 | 
			
		||||
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 drawing_modules.draw_widget_solve import SketchWidget
 | 
			
		||||
from sdf import *
 | 
			
		||||
from python_solvespace import SolverSystem, ResultFlag
 | 
			
		||||
from mesh_modules import simple_mesh, vesta_mesh, interactor_mesh
 | 
			
		||||
@@ -294,7 +294,10 @@ class MainWindow(QMainWindow):
 | 
			
		||||
        self.custom_3D_Widget.clear_actors_normals()
 | 
			
		||||
 | 
			
		||||
    def add_sketch(self):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        sketch = self.sketchWidget.get_sketch()
 | 
			
		||||
        sketch.convert_points_for_sdf()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user