Fix sketcher mode handling to prevent unintended line creation during drag operations
Major changes: - Fixed right-click handler to directly set mode to NONE instead of relying on main app signal handling - Added safety checks in left-click handler to prevent drawing when no draggable point is found in NONE mode - Enhanced mode compatibility by treating Python None as SketchMode.NONE in set_mode() method - Added comprehensive debug logging for mode changes and interaction state tracking - Resolved integration issue where persistent constraint modes were prematurely reset by main app - Ensured point dragging is only enabled in NONE mode, preventing accidental polyline creation This fixes the reported issue where deactivating the line tool would still create lines when dragging, and ensures proper mode transitions between drawing tools and selection/drag mode.
This commit is contained in:
22
.idea/workspace.xml
generated
22
.idea/workspace.xml
generated
@@ -4,14 +4,11 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="8f0bafd6-58a0-4b20-aa2b-ddc3ba278873" name="Changes" comment="- added screenshot">
|
<list default="true" id="8f0bafd6-58a0-4b20-aa2b-ddc3ba278873" name="Changes" comment="- added sdf folder ( doesnt work via pip or git=)">
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/fluency.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/fluency.iml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/drawing_modules/draw_widget_solve.py" beforeDir="false" afterPath="$PROJECT_DIR$/drawing_modules/draw_widget_solve.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/mesh_modules/simple_mesh.py" beforeDir="false" afterPath="$PROJECT_DIR$/mesh_modules/simple_mesh.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/mesh_modules/interactor_mesh.py" beforeDir="false" afterPath="$PROJECT_DIR$/mesh_modules/interactor_mesh.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -247,7 +244,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1748765318267</updated>
|
<updated>1748765318267</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="19" />
|
<task id="LOCAL-00019" summary="- added sdf folder ( doesnt work via pip or git=)">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1755369224187</created>
|
||||||
|
<option name="number" value="00019" />
|
||||||
|
<option name="presentableId" value="LOCAL-00019" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1755369224187</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="20" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
<component name="Vcs.Log.Tabs.Properties">
|
||||||
@@ -281,6 +286,7 @@
|
|||||||
<MESSAGE value="- Fixed redraw when component changed" />
|
<MESSAGE value="- Fixed redraw when component changed" />
|
||||||
<MESSAGE value="- added MIT license" />
|
<MESSAGE value="- added MIT license" />
|
||||||
<MESSAGE value="- added screenshot" />
|
<MESSAGE value="- added screenshot" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="- added screenshot" />
|
<MESSAGE value="- added sdf folder ( doesnt work via pip or git=)" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="- added sdf folder ( doesnt work via pip or git=)" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
147
WARP.md
Normal file
147
WARP.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# WARP.md
|
||||||
|
|
||||||
|
This file provides guidance to WARP (warp.dev) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
Fluency is a CAD (Computer Aided Design) application built with Python/PySide6 that provides parametric 3D modeling through a timeline-based project system. The application combines 2D sketching with constraint solving, 3D visualization using VTK, and SDF (Signed Distance Function) based mesh generation.
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
### Development Environment Setup
|
||||||
|
```bash
|
||||||
|
# Activate virtual environment (if exists)
|
||||||
|
source .venv/bin/activate
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the Application
|
||||||
|
```bash
|
||||||
|
# Run the main application
|
||||||
|
python main.py
|
||||||
|
|
||||||
|
# Run with debugging
|
||||||
|
python -u main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI Development
|
||||||
|
```bash
|
||||||
|
# Convert Qt Designer UI file to Python code
|
||||||
|
pyside6-uic gui.ui > Gui.py -g python
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building Executable
|
||||||
|
The project uses Nuitka for compilation (configured in `main.py` header):
|
||||||
|
```bash
|
||||||
|
# Build standalone executable
|
||||||
|
nuitka --standalone --plugin-enable=pyside6 --plugin-enable=numpy --macos-create-app-bundle main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
# Run mesh generation test
|
||||||
|
python meshtest.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
#### Main Application (`main.py`)
|
||||||
|
- **MainWindow**: Central UI controller that manages all widgets and user interactions
|
||||||
|
- **Project System**: Hierarchical structure: `Project → Timeline → Component → Sketch/Body`
|
||||||
|
- **Signal-based Communication**: Qt signals coordinate between 2D sketching and 3D rendering
|
||||||
|
|
||||||
|
#### Project Hierarchy
|
||||||
|
```
|
||||||
|
Project
|
||||||
|
├── Timeline (list of Components)
|
||||||
|
└── Component
|
||||||
|
├── Sketches (dict)
|
||||||
|
├── Bodies (dict)
|
||||||
|
└── Connectors (for assembly)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Drawing Modules (`drawing_modules/`)
|
||||||
|
- **SketchWidget** (`draw_widget_solve.py`): 2D parametric sketching with SolverSpace constraint solving
|
||||||
|
- **VTKWidget** (`vtk_widget.py`): 3D visualization and mesh interaction using VTK
|
||||||
|
- **PyVistaWidget** (`vysta_widget.py`): Alternative 3D rendering backend
|
||||||
|
|
||||||
|
#### Mesh Generation (`mesh_modules/`)
|
||||||
|
- **VESTA** (`vesta_mesh.py`): Multi-threaded SDF-to-mesh conversion using marching cubes
|
||||||
|
- **Interactor Mesh** (`interactor_mesh.py`): Simplified edge-based meshes for 3D selection
|
||||||
|
- **Simple Mesh** (`simple_mesh.py`): Basic mesh utilities
|
||||||
|
|
||||||
|
### Data Flow Architecture
|
||||||
|
|
||||||
|
#### 2D to 3D Pipeline
|
||||||
|
1. **2D Sketching**: User draws in SketchWidget using Qt coordinate system
|
||||||
|
2. **Constraint Solving**: SolverSpace resolves geometric constraints
|
||||||
|
3. **SDF Generation**: Sketch converted to Signed Distance Functions for 3D operations
|
||||||
|
4. **Mesh Generation**: VESTA generates triangle meshes from SDF using marching cubes
|
||||||
|
5. **3D Rendering**: VTK displays both solid meshes and interactive edges
|
||||||
|
|
||||||
|
#### Signal Flow (from `doc/flow.md`)
|
||||||
|
- 2D QPoint → cartesian space → SolverSpace dict → constraint solving → display
|
||||||
|
- 3D mesh selection → projection to 2D → sketch widget integration
|
||||||
|
|
||||||
|
### Key Classes
|
||||||
|
|
||||||
|
#### Core Data Structures
|
||||||
|
- **Sketch**: 2D geometric data with origin, normal, points, and constraints
|
||||||
|
- **Body**: 3D mesh representation containing SDF objects and interactor meshes
|
||||||
|
- **Component**: Container grouping related sketches and bodies
|
||||||
|
- **Interactor**: Simplified edge-based mesh for 3D manipulation
|
||||||
|
|
||||||
|
#### Constraint Solving
|
||||||
|
The application uses `python_solvespace` for parametric constraint solving:
|
||||||
|
- Point-to-point constraints
|
||||||
|
- Distance constraints
|
||||||
|
- Horizontal/vertical line constraints
|
||||||
|
- Point-to-line constraints
|
||||||
|
|
||||||
|
### Technology Stack
|
||||||
|
- **GUI**: PySide6 (Qt for Python)
|
||||||
|
- **3D Graphics**: VTK for rendering, PyVista as alternative
|
||||||
|
- **Constraint Solving**: SolverSpace for parametric geometry
|
||||||
|
- **Mesh Generation**: SDF library with custom VESTA marching cubes implementation
|
||||||
|
- **Scientific Computing**: NumPy for mathematical operations
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Adding New Sketch Tools
|
||||||
|
1. Add UI button in `gui.ui`
|
||||||
|
2. Convert UI: `pyside6-uic gui.ui > Gui.py -g python`
|
||||||
|
3. Connect signal in `MainWindow.__init__()`
|
||||||
|
4. Implement tool logic in `SketchWidget`
|
||||||
|
|
||||||
|
### Adding New 3D Operations
|
||||||
|
1. Extend operation buttons in the Modify group
|
||||||
|
2. Implement operation logic using SDF functions
|
||||||
|
3. Update Body creation and timeline management
|
||||||
|
4. Handle interactor mesh generation for selection
|
||||||
|
|
||||||
|
### Debugging Tips
|
||||||
|
- Monitor solver results through `SolverSystem` status
|
||||||
|
- Use VTK's built-in debugging for rendering issues
|
||||||
|
- Check coordinate transformations between 2D sketch and 3D space
|
||||||
|
- Verify SDF function outputs before mesh generation
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
- `main.py`: Application entry point and main window
|
||||||
|
- `Gui.py`: Auto-generated UI code (do not edit directly)
|
||||||
|
- `gui.ui`: Qt Designer UI definition file
|
||||||
|
- `drawing_modules/`: 2D and 3D rendering widgets
|
||||||
|
- `mesh_modules/`: Mesh generation and processing
|
||||||
|
- `doc/`: Architecture and command documentation
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
Primary external libraries:
|
||||||
|
- `PySide6`: Qt GUI framework
|
||||||
|
- `vtk`: 3D visualization toolkit
|
||||||
|
- `python-solvespace`: Constraint solving
|
||||||
|
- `sdf`: Signed Distance Function operations
|
||||||
|
- `numpy`: Numerical computations
|
||||||
|
- `scikit-image`: Marching cubes algorithm
|
||||||
|
- `names`: Random name generation for sketches
|
||||||
BIN
__pycache__/Gui.cpython-311.pyc
Normal file
BIN
__pycache__/Gui.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/Gui.cpython-312.pyc
Normal file
BIN
__pycache__/Gui.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/main.cpython-311.pyc
Normal file
BIN
__pycache__/main.cpython-311.pyc
Normal file
Binary file not shown.
BIN
__pycache__/main.cpython-312.pyc
Normal file
BIN
__pycache__/main.cpython-312.pyc
Normal file
Binary file not shown.
108
debug_dragging.py
Normal file
108
debug_dragging.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Debug script to test point dragging functionality in ImprovedSketchWidget
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.append('/Volumes/Data_drive/Programming/fluency')
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton, QHBoxLayout
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from drawing_modules.improved_sketcher import ImprovedSketchWidget, SketchMode, Point2D
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Set up logging to see debug messages
|
||||||
|
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class DebugMainWindow(QMainWindow):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("Debug Point Dragging")
|
||||||
|
self.resize(1000, 700)
|
||||||
|
|
||||||
|
# Create central widget
|
||||||
|
central_widget = QWidget()
|
||||||
|
self.setCentralWidget(central_widget)
|
||||||
|
layout = QVBoxLayout(central_widget)
|
||||||
|
|
||||||
|
# Create button layout
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
# Add test points button
|
||||||
|
add_points_btn = QPushButton("Add Test Points")
|
||||||
|
add_points_btn.clicked.connect(self.add_test_points)
|
||||||
|
button_layout.addWidget(add_points_btn)
|
||||||
|
|
||||||
|
# Check mode button
|
||||||
|
check_mode_btn = QPushButton("Check Mode")
|
||||||
|
check_mode_btn.clicked.connect(self.check_mode)
|
||||||
|
button_layout.addWidget(check_mode_btn)
|
||||||
|
|
||||||
|
# Reset mode button
|
||||||
|
reset_mode_btn = QPushButton("Reset to NONE Mode")
|
||||||
|
reset_mode_btn.clicked.connect(self.reset_mode)
|
||||||
|
button_layout.addWidget(reset_mode_btn)
|
||||||
|
|
||||||
|
layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
# Create the sketcher widget
|
||||||
|
self.sketcher = ImprovedSketchWidget()
|
||||||
|
layout.addWidget(self.sketcher)
|
||||||
|
|
||||||
|
print("Debug window created. Current mode:", self.sketcher.current_mode)
|
||||||
|
|
||||||
|
def add_test_points(self):
|
||||||
|
"""Add some test points to the sketch"""
|
||||||
|
print("Adding test points...")
|
||||||
|
|
||||||
|
# Add a few points at different locations
|
||||||
|
points = [
|
||||||
|
Point2D(100, 100),
|
||||||
|
Point2D(200, 150),
|
||||||
|
Point2D(150, 200),
|
||||||
|
Point2D(50, 250)
|
||||||
|
]
|
||||||
|
|
||||||
|
for point in points:
|
||||||
|
self.sketcher.sketch.add_point(point)
|
||||||
|
print(f"Added point at ({point.x}, {point.y})")
|
||||||
|
|
||||||
|
self.sketcher.update()
|
||||||
|
print(f"Total points in sketch: {len(self.sketcher.sketch.points)}")
|
||||||
|
|
||||||
|
def check_mode(self):
|
||||||
|
"""Check current mode and dragging state"""
|
||||||
|
print(f"Current mode: {self.sketcher.current_mode}")
|
||||||
|
print(f"Dragging point: {self.sketcher.dragging_point}")
|
||||||
|
print(f"Drag start pos: {self.sketcher.drag_start_pos}")
|
||||||
|
print(f"Hovered point: {self.sketcher.hovered_point}")
|
||||||
|
print(f"Number of points: {len(self.sketcher.sketch.points)}")
|
||||||
|
|
||||||
|
# Check if points have solver handles
|
||||||
|
for i, point in enumerate(self.sketcher.sketch.points):
|
||||||
|
print(f"Point {i}: ({point.x}, {point.y}), handle: {point.handle}")
|
||||||
|
|
||||||
|
def reset_mode(self):
|
||||||
|
"""Reset to NONE mode to enable dragging"""
|
||||||
|
print("Resetting mode to NONE")
|
||||||
|
self.sketcher.set_mode(SketchMode.NONE)
|
||||||
|
print(f"Mode after reset: {self.sketcher.current_mode}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
window = DebugMainWindow()
|
||||||
|
window.show()
|
||||||
|
|
||||||
|
print("\n" + "="*50)
|
||||||
|
print("DEBUG INSTRUCTIONS:")
|
||||||
|
print("1. Click 'Add Test Points' to create some points")
|
||||||
|
print("2. Click 'Check Mode' to verify the current state")
|
||||||
|
print("3. Click 'Reset to NONE Mode' to ensure dragging is enabled")
|
||||||
|
print("4. Try to drag points by clicking and dragging them")
|
||||||
|
print("5. Watch the console for debug messages")
|
||||||
|
print("="*50 + "\n")
|
||||||
|
|
||||||
|
sys.exit(app.exec())
|
||||||
697
doc/SKETCHER_TECHNICAL_DOCUMENTATION.md
Normal file
697
doc/SKETCHER_TECHNICAL_DOCUMENTATION.md
Normal file
@@ -0,0 +1,697 @@
|
|||||||
|
# Fluency CAD - Improved Sketcher Technical Documentation
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Architecture](#architecture)
|
||||||
|
3. [Core Components](#core-components)
|
||||||
|
4. [Geometry System](#geometry-system)
|
||||||
|
5. [Constraint Solving](#constraint-solving)
|
||||||
|
6. [Coordinate Systems](#coordinate-systems)
|
||||||
|
7. [Interaction System](#interaction-system)
|
||||||
|
8. [Rendering System](#rendering-system)
|
||||||
|
9. [Snapping System](#snapping-system)
|
||||||
|
10. [Working Plane Integration](#working-plane-integration)
|
||||||
|
11. [API Reference](#api-reference)
|
||||||
|
12. [Performance Considerations](#performance-considerations)
|
||||||
|
13. [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The ImprovedSketchWidget is a parametric 2D sketching system built for Fluency CAD. It provides constraint-based geometric modeling with real-time solving, integrated snapping, and seamless integration with 3D working planes. The system is built on top of the SolverSpace constraint solver and PySide6 for the user interface.
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
- **Parametric Geometry**: All geometry is constraint-driven and automatically updates
|
||||||
|
- **Real-time Solving**: Constraints are solved dynamically as geometry is modified
|
||||||
|
- **Advanced Snapping**: Multi-mode snapping system (points, midpoints, grid, angles)
|
||||||
|
- **Construction Geometry**: Support for helper/construction geometry
|
||||||
|
- **Working Plane Integration**: Seamless 2D/3D workflow with projected geometry
|
||||||
|
- **Interactive Dragging**: Smooth point dragging with constraint preservation
|
||||||
|
- **Multiple Drawing Modes**: Lines, rectangles, circles, arcs, and points
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ ImprovedSketchWidget │
|
||||||
|
│ ┌─────────────────┐ ┌─────────────────────────────┐ │
|
||||||
|
│ │ User Interface │ │ Rendering System │ │
|
||||||
|
│ │ - Mouse Events │ │ - Coordinate Transform │ │
|
||||||
|
│ │ - Keyboard │ │ - Geometry Drawing │ │
|
||||||
|
│ │ - Mode Control │ │ - UI Overlays │ │
|
||||||
|
│ └─────────────────┘ └─────────────────────────────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └─────────┬───────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Interaction System │ │
|
||||||
|
│ │ - Snapping Engine │ │
|
||||||
|
│ │ - Dragging Logic │ │
|
||||||
|
│ │ - Selection Management │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Geometry System │ │
|
||||||
|
│ │ ┌─────────────┐ ┌─────────────────────────┐ │ │
|
||||||
|
│ │ │ Point2D │ │ Line2D │ │ │
|
||||||
|
│ │ │ Circle2D │ │ Arc2D (future) │ │ │
|
||||||
|
│ │ └─────────────┘ └─────────────────────────┘ │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ ImprovedSketch │ │
|
||||||
|
│ │ (Enhanced SolverSystem) │ │
|
||||||
|
│ │ - Constraint Management │ │
|
||||||
|
│ │ - Solver Integration │ │
|
||||||
|
│ │ - Geometry Storage │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┐ │
|
||||||
|
│ │ SolverSpace Library │ │
|
||||||
|
│ │ - Constraint Solving Engine │ │
|
||||||
|
│ │ - Geometric Relationships │ │
|
||||||
|
│ └─────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. ImprovedSketchWidget
|
||||||
|
The main widget class that handles user interaction and rendering.
|
||||||
|
|
||||||
|
**Key Responsibilities:**
|
||||||
|
- Mouse and keyboard event handling
|
||||||
|
- Mode management (line, circle, constraint modes, etc.)
|
||||||
|
- Coordinate system transformations
|
||||||
|
- Rendering pipeline orchestration
|
||||||
|
- Integration with external systems (working planes)
|
||||||
|
|
||||||
|
### 2. ImprovedSketch
|
||||||
|
Enhanced wrapper around SolverSpace's SolverSystem.
|
||||||
|
|
||||||
|
**Key Responsibilities:**
|
||||||
|
- Geometry storage and management
|
||||||
|
- Constraint system integration
|
||||||
|
- Solver result processing
|
||||||
|
- Handle management for solver objects
|
||||||
|
|
||||||
|
### 3. Geometry Classes
|
||||||
|
Type-safe geometry representations with validation.
|
||||||
|
|
||||||
|
**Classes:**
|
||||||
|
- `Point2D`: 2D points with solver integration
|
||||||
|
- `Line2D`: 2D lines with constraint tracking
|
||||||
|
- `Circle2D`: 2D circles with radius constraints
|
||||||
|
|
||||||
|
## Geometry System
|
||||||
|
|
||||||
|
### Point2D Class
|
||||||
|
```python
|
||||||
|
class Point2D:
|
||||||
|
def __init__(self, x: float, y: float, is_construction: bool = False):
|
||||||
|
self.id = uuid.uuid4() # Unique identifier
|
||||||
|
self.x = float(x) # X coordinate
|
||||||
|
self.y = float(y) # Y coordinate
|
||||||
|
self.ui_point = QPoint(int(x), int(y)) # Qt UI point
|
||||||
|
self.handle = None # SolverSpace handle
|
||||||
|
self.handle_nr = None # Handle number
|
||||||
|
self.is_helper = is_construction # Construction geometry flag
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Automatic coordinate validation
|
||||||
|
- SolverSpace handle integration
|
||||||
|
- Construction/normal geometry support
|
||||||
|
- Distance calculations and equality testing
|
||||||
|
|
||||||
|
### Line2D Class
|
||||||
|
```python
|
||||||
|
class Line2D:
|
||||||
|
def __init__(self, start_point: Point2D, end_point: Point2D, is_construction: bool = False):
|
||||||
|
self.id = uuid.uuid4()
|
||||||
|
self.start = start_point # Start point reference
|
||||||
|
self.end = end_point # End point reference
|
||||||
|
self.handle = None # SolverSpace handle
|
||||||
|
self.constraints = [] # Applied constraints list
|
||||||
|
self.is_helper = is_construction
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Automatic degenerate line detection
|
||||||
|
- Length, midpoint, and angle calculations
|
||||||
|
- Point-on-line testing with tolerance
|
||||||
|
- Constraint tracking and annotation
|
||||||
|
|
||||||
|
### Circle2D Class
|
||||||
|
```python
|
||||||
|
class Circle2D:
|
||||||
|
def __init__(self, center: Point2D, radius: float, is_construction: bool = False):
|
||||||
|
self.id = uuid.uuid4()
|
||||||
|
self.center = center # Center point reference
|
||||||
|
self.radius = float(radius) # Radius value
|
||||||
|
self.handle = None # SolverSpace handle
|
||||||
|
self.constraints = [] # Applied constraints
|
||||||
|
self.is_helper = is_construction
|
||||||
|
```
|
||||||
|
|
||||||
|
## Constraint Solving
|
||||||
|
|
||||||
|
### SolverSpace Integration
|
||||||
|
|
||||||
|
The system uses the `python-solvespace` library for constraint solving. The `ImprovedSketch` class wraps the SolverSpace API and provides:
|
||||||
|
|
||||||
|
1. **Automatic Handle Management**: Each geometry object gets a unique handle
|
||||||
|
2. **Error Handling**: Robust error handling for solver failures
|
||||||
|
3. **Position Updates**: Automatic geometry position updates after solving
|
||||||
|
|
||||||
|
### Constraint Types
|
||||||
|
|
||||||
|
#### Geometric Constraints
|
||||||
|
- **Coincident**: Point-to-point or point-to-line coincidence
|
||||||
|
- **Horizontal**: Forces lines to be horizontal
|
||||||
|
- **Vertical**: Forces lines to be vertical
|
||||||
|
- **Distance**: Fixes distance between points or line length
|
||||||
|
- **Parallel**: Makes lines parallel (future implementation)
|
||||||
|
- **Perpendicular**: Makes lines perpendicular (future implementation)
|
||||||
|
|
||||||
|
#### Constraint Application Workflow
|
||||||
|
```python
|
||||||
|
def _handle_distance_constraint(self, pos: QPoint):
|
||||||
|
line = self.sketch.get_line_near(pos)
|
||||||
|
if line and line.handle:
|
||||||
|
# Get user input for distance
|
||||||
|
distance, ok = QInputDialog.getDouble(...)
|
||||||
|
if ok:
|
||||||
|
# Apply constraint to solver
|
||||||
|
self.sketch.distance(line.start.handle, line.end.handle, distance, self.sketch.wp)
|
||||||
|
# Solve system
|
||||||
|
result = self.sketch.solve_system()
|
||||||
|
if result == ResultFlag.OKAY:
|
||||||
|
line.constraints.append(f"L={distance:.2f}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Solver Workflow
|
||||||
|
|
||||||
|
1. **Constraint Addition**: Constraints are added to the solver system
|
||||||
|
2. **System Solving**: The solver attempts to find a valid solution
|
||||||
|
3. **Result Processing**: If successful, geometry positions are updated
|
||||||
|
4. **UI Updates**: The display is refreshed to show new positions
|
||||||
|
|
||||||
|
## Coordinate Systems
|
||||||
|
|
||||||
|
The sketcher uses multiple coordinate systems that must be properly transformed between:
|
||||||
|
|
||||||
|
### 1. Sketch Coordinates (Local)
|
||||||
|
- Origin at sketch center
|
||||||
|
- Y-axis points up (mathematical convention)
|
||||||
|
- Units in millimeters
|
||||||
|
- Range: typically -1000 to +1000
|
||||||
|
|
||||||
|
### 2. Viewport Coordinates (Screen)
|
||||||
|
- Origin at top-left of widget
|
||||||
|
- Y-axis points down (computer graphics convention)
|
||||||
|
- Units in pixels
|
||||||
|
- Range: 0 to widget dimensions
|
||||||
|
|
||||||
|
### 3. Working Plane Coordinates (3D)
|
||||||
|
- 3D coordinates projected onto 2D working plane
|
||||||
|
- Transformation handled by external VTK system
|
||||||
|
- Converted to sketch coordinates for display
|
||||||
|
|
||||||
|
### Coordinate Transformations
|
||||||
|
|
||||||
|
#### Viewport to Local (Mouse Input)
|
||||||
|
```python
|
||||||
|
def _viewport_to_local(self, viewport_pos: QPoint) -> QPoint:
|
||||||
|
# Step 1: Subtract widget center
|
||||||
|
center_x = self.width() / 2
|
||||||
|
center_y = self.height() / 2
|
||||||
|
|
||||||
|
# Step 2: Apply pan offset
|
||||||
|
viewport_x = viewport_pos.x() - center_x - (self.pan_offset.x() * self.zoom_factor)
|
||||||
|
viewport_y = viewport_pos.y() - center_y - (self.pan_offset.y() * self.zoom_factor)
|
||||||
|
|
||||||
|
# Step 3: Apply inverse zoom and Y-flip
|
||||||
|
local_x = viewport_x / self.zoom_factor
|
||||||
|
local_y = -viewport_y / self.zoom_factor
|
||||||
|
|
||||||
|
return QPoint(int(local_x), int(local_y))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rendering Transform Setup
|
||||||
|
```python
|
||||||
|
def _setup_coordinate_system(self, painter: QPainter):
|
||||||
|
transform = QTransform()
|
||||||
|
|
||||||
|
# Translate to center and apply pan
|
||||||
|
center = QPointF(self.width() / 2, self.height() / 2)
|
||||||
|
transform.translate(center.x() + self.pan_offset.x() * self.zoom_factor,
|
||||||
|
center.y() + self.pan_offset.y() * self.zoom_factor)
|
||||||
|
|
||||||
|
# Apply zoom and flip Y-axis
|
||||||
|
transform.scale(self.zoom_factor, -self.zoom_factor)
|
||||||
|
|
||||||
|
painter.setTransform(transform)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interaction System
|
||||||
|
|
||||||
|
### Mode-Based Interaction
|
||||||
|
|
||||||
|
The sketcher supports multiple interaction modes:
|
||||||
|
|
||||||
|
#### Drawing Modes
|
||||||
|
- `SketchMode.LINE`: Two-point line creation
|
||||||
|
- `SketchMode.RECTANGLE`: Two-corner rectangle creation
|
||||||
|
- `SketchMode.CIRCLE`: Center-radius circle creation
|
||||||
|
- `SketchMode.POINT`: Single point creation
|
||||||
|
|
||||||
|
#### Constraint Modes
|
||||||
|
- `SketchMode.COINCIDENT_PT_PT`: Point-to-point coincidence
|
||||||
|
- `SketchMode.HORIZONTAL`: Horizontal line constraint
|
||||||
|
- `SketchMode.VERTICAL`: Vertical line constraint
|
||||||
|
- `SketchMode.DISTANCE`: Distance/length constraint
|
||||||
|
|
||||||
|
#### Selection Mode
|
||||||
|
- `SketchMode.NONE`: Selection and manipulation mode
|
||||||
|
|
||||||
|
### Mouse Event Handling
|
||||||
|
|
||||||
|
#### Click Processing Flow
|
||||||
|
```python
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
local_pos = self._viewport_to_local(event.pos())
|
||||||
|
|
||||||
|
if event.button() == Qt.LeftButton:
|
||||||
|
if self.current_mode == SketchMode.NONE:
|
||||||
|
# Check for point dragging
|
||||||
|
point = self.sketch.get_point_near(local_pos)
|
||||||
|
if point:
|
||||||
|
self._start_point_drag(point, local_pos)
|
||||||
|
else:
|
||||||
|
# Handle drawing modes
|
||||||
|
self._handle_left_click(local_pos)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Point Dragging System
|
||||||
|
|
||||||
|
The point dragging system is optimized for performance and maintains constraint consistency:
|
||||||
|
|
||||||
|
#### Drag Phases
|
||||||
|
|
||||||
|
1. **Drag Start** (`_start_point_drag`):
|
||||||
|
- Identifies dragged point
|
||||||
|
- Stores initial position
|
||||||
|
- Sets dragging state
|
||||||
|
|
||||||
|
2. **Drag Update** (`_handle_point_drag`):
|
||||||
|
- Updates point visual position only
|
||||||
|
- Applies snapping
|
||||||
|
- No solver execution (for performance)
|
||||||
|
|
||||||
|
3. **Drag End** (`_end_point_drag`):
|
||||||
|
- Updates solver parameters with final position
|
||||||
|
- Runs constraint solver
|
||||||
|
- Updates all connected geometry
|
||||||
|
- Resets drag state
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _end_point_drag(self):
|
||||||
|
if not self.dragging_point:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Update solver parameters with final position
|
||||||
|
if self.dragging_point.handle:
|
||||||
|
new_x = self.dragging_point.x
|
||||||
|
new_y = self.dragging_point.y
|
||||||
|
self.sketch.set_params(self.dragging_point.handle.params, [new_x, new_y])
|
||||||
|
|
||||||
|
# Run solver to update all connected geometry
|
||||||
|
result = self.sketch.solve_system()
|
||||||
|
if result == ResultFlag.OKAY:
|
||||||
|
self.sketch_modified.emit()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rendering System
|
||||||
|
|
||||||
|
### Rendering Pipeline
|
||||||
|
|
||||||
|
The rendering system uses Qt's QPainter with a multi-layer approach:
|
||||||
|
|
||||||
|
1. **Coordinate System Setup**: Apply zoom, pan, and Y-flip transforms
|
||||||
|
2. **Background Rendering**: Grid, axes, and origin marker
|
||||||
|
3. **Geometry Rendering**: Points, lines, circles with proper styling
|
||||||
|
4. **Dynamic Elements**: Preview geometry during creation
|
||||||
|
5. **UI Overlays**: Mode indicators, measurements, snap highlights
|
||||||
|
|
||||||
|
### Rendering Layers
|
||||||
|
|
||||||
|
#### Layer 1: Background
|
||||||
|
- Coordinate axes (dashed gray lines)
|
||||||
|
- Grid (if enabled)
|
||||||
|
- Origin marker (red circle)
|
||||||
|
|
||||||
|
#### Layer 2: Geometry
|
||||||
|
- Construction geometry (green, dotted)
|
||||||
|
- Normal geometry (gray, solid)
|
||||||
|
- Constraint annotations
|
||||||
|
|
||||||
|
#### Layer 3: Interactive Elements
|
||||||
|
- Hover highlights (red)
|
||||||
|
- Dynamic previews (gray, dashed)
|
||||||
|
- Measurements during creation
|
||||||
|
|
||||||
|
#### Layer 4: UI Overlays
|
||||||
|
- Snap point indicators
|
||||||
|
- Mode and zoom information
|
||||||
|
- Status messages
|
||||||
|
|
||||||
|
### Styling System
|
||||||
|
|
||||||
|
Rendering appearance is controlled by the `RenderSettings` class:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class RenderSettings:
|
||||||
|
normal_pen_width: float = 2.0
|
||||||
|
construction_pen_width: float = 1.0
|
||||||
|
highlight_pen_width: float = 3.0
|
||||||
|
|
||||||
|
normal_color = QColor(128, 128, 128) # Gray
|
||||||
|
construction_color = QColor(0, 255, 0) # Green
|
||||||
|
highlight_color = QColor(255, 0, 0) # Red
|
||||||
|
solver_color = QColor(0, 255, 0) # Green
|
||||||
|
dynamic_color = QColor(128, 128, 128) # Gray
|
||||||
|
text_color = QColor(255, 255, 255) # White
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamic Previews
|
||||||
|
|
||||||
|
During geometry creation, dynamic previews show:
|
||||||
|
- **Line Creation**: Dashed line from start to cursor with length annotation
|
||||||
|
- **Rectangle Creation**: Dashed rectangle outline
|
||||||
|
- **Circle Creation**: Dashed circle with radius line and annotation
|
||||||
|
|
||||||
|
## Snapping System
|
||||||
|
|
||||||
|
### Snap Modes
|
||||||
|
|
||||||
|
The snapping system supports multiple simultaneous snap modes:
|
||||||
|
|
||||||
|
#### SnapMode.POINT
|
||||||
|
- Snaps to existing geometry points
|
||||||
|
- Priority: Highest
|
||||||
|
- Visual: Red circle highlight
|
||||||
|
|
||||||
|
#### SnapMode.MIDPOINT
|
||||||
|
- Snaps to line midpoints
|
||||||
|
- Priority: Medium
|
||||||
|
- Visual: Red diamond highlight
|
||||||
|
|
||||||
|
#### SnapMode.GRID
|
||||||
|
- Snaps to grid intersections
|
||||||
|
- Priority: Lowest
|
||||||
|
- Visual: Green cross highlight
|
||||||
|
|
||||||
|
#### SnapMode.HORIZONTAL/VERTICAL
|
||||||
|
- Angular snapping (future implementation)
|
||||||
|
- Constrains to horizontal/vertical directions
|
||||||
|
|
||||||
|
#### SnapMode.INTERSECTION
|
||||||
|
- Snaps to line intersections (future implementation)
|
||||||
|
|
||||||
|
### Snap Algorithm
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _get_snapped_position(self, pos: QPoint) -> QPoint:
|
||||||
|
min_distance = float('inf')
|
||||||
|
snapped_pos = pos
|
||||||
|
snap_threshold = self.snap_settings.snap_distance
|
||||||
|
|
||||||
|
# Point snapping (highest priority)
|
||||||
|
if SnapMode.POINT in self.snap_settings.enabled_modes:
|
||||||
|
for point in self.sketch.points:
|
||||||
|
distance = math.sqrt((pos.x() - point.x)**2 + (pos.y() - point.y)**2)
|
||||||
|
if distance < snap_threshold and distance < min_distance:
|
||||||
|
snapped_pos = QPoint(int(point.x), int(point.y))
|
||||||
|
min_distance = distance
|
||||||
|
|
||||||
|
# Midpoint snapping (medium priority)
|
||||||
|
if SnapMode.MIDPOINT in self.snap_settings.enabled_modes and min_distance > snap_threshold:
|
||||||
|
for line in self.sketch.lines:
|
||||||
|
midpoint = line.midpoint
|
||||||
|
distance = math.sqrt((pos.x() - midpoint.x)**2 + (pos.y() - midpoint.y)**2)
|
||||||
|
if distance < snap_threshold and distance < min_distance:
|
||||||
|
snapped_pos = QPoint(int(midpoint.x), int(midpoint.y))
|
||||||
|
min_distance = distance
|
||||||
|
|
||||||
|
return snapped_pos
|
||||||
|
```
|
||||||
|
|
||||||
|
### Snap Settings
|
||||||
|
|
||||||
|
```python
|
||||||
|
@dataclass
|
||||||
|
class SnapSettings:
|
||||||
|
snap_distance: float = 20.0 # Snap threshold in pixels
|
||||||
|
angle_increment: float = 15.0 # Angular snap increment
|
||||||
|
grid_spacing: float = 50.0 # Grid spacing
|
||||||
|
enabled_modes: Set[SnapMode] # Active snap modes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Working Plane Integration
|
||||||
|
|
||||||
|
### Projected Geometry Workflow
|
||||||
|
|
||||||
|
The sketcher integrates with 3D working planes through projected geometry:
|
||||||
|
|
||||||
|
1. **3D Geometry Selection**: User selects 3D lines/points in VTK widget
|
||||||
|
2. **Plane Definition**: System computes working plane from selections
|
||||||
|
3. **Geometry Projection**: 3D geometry is projected onto 2D working plane
|
||||||
|
4. **Sketch Import**: Projected geometry is imported as construction geometry
|
||||||
|
|
||||||
|
### Projection Import Methods
|
||||||
|
|
||||||
|
#### `convert_proj_points(proj_points)`
|
||||||
|
Imports projected 3D points as 2D construction points:
|
||||||
|
```python
|
||||||
|
def convert_proj_points(self, proj_points):
|
||||||
|
for point_data in proj_points:
|
||||||
|
if hasattr(point_data, 'x') and hasattr(point_data, 'y'):
|
||||||
|
point = Point2D(point_data.x, point_data.y, True) # Construction
|
||||||
|
self.sketch.add_point(point)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `convert_proj_lines(proj_lines)`
|
||||||
|
Imports projected 3D lines as 2D construction lines:
|
||||||
|
```python
|
||||||
|
def convert_proj_lines(self, proj_lines):
|
||||||
|
for line_data in proj_lines:
|
||||||
|
# Handle object format
|
||||||
|
if hasattr(line_data, 'start') and hasattr(line_data, 'end'):
|
||||||
|
x1, y1 = line_data.start.x, line_data.start.y
|
||||||
|
x2, y2 = line_data.end.x, line_data.end.y
|
||||||
|
|
||||||
|
# Skip degenerate lines
|
||||||
|
if abs(x1 - x2) < 1e-6 and abs(y1 - y2) < 1e-6:
|
||||||
|
continue
|
||||||
|
|
||||||
|
start = Point2D(x1, y1, True)
|
||||||
|
end = Point2D(x2, y2, True)
|
||||||
|
self.sketch.add_point(start)
|
||||||
|
self.sketch.add_point(end)
|
||||||
|
line = Line2D(start, end, True)
|
||||||
|
self.sketch.add_line(line)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Construction vs Normal Geometry
|
||||||
|
|
||||||
|
- **Construction Geometry**:
|
||||||
|
- Rendered in green with dotted lines
|
||||||
|
- Used for reference and alignment
|
||||||
|
- Created from projected 3D geometry
|
||||||
|
- Flag: `is_construction=True`
|
||||||
|
|
||||||
|
- **Normal Geometry**:
|
||||||
|
- Rendered in gray with solid lines
|
||||||
|
- Part of the actual sketch design
|
||||||
|
- Created by user drawing actions
|
||||||
|
- Flag: `is_construction=False`
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Main Widget Class
|
||||||
|
|
||||||
|
#### ImprovedSketchWidget
|
||||||
|
|
||||||
|
**Initialization:**
|
||||||
|
```python
|
||||||
|
widget = ImprovedSketchWidget()
|
||||||
|
widget.show()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mode Control:**
|
||||||
|
```python
|
||||||
|
widget.set_mode(SketchMode.LINE)
|
||||||
|
widget.set_construction_mode(True)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Snapping Control:**
|
||||||
|
```python
|
||||||
|
widget.set_snap_mode(SnapMode.POINT, True)
|
||||||
|
widget.toggle_snap_mode(SnapMode.MIDPOINT, enabled)
|
||||||
|
```
|
||||||
|
|
||||||
|
**View Control:**
|
||||||
|
```python
|
||||||
|
widget.zoom_to_fit()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sketch Access:**
|
||||||
|
```python
|
||||||
|
sketch = widget.get_sketch()
|
||||||
|
widget.set_sketch(imported_sketch)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sketch Management
|
||||||
|
|
||||||
|
#### ImprovedSketch
|
||||||
|
|
||||||
|
**Geometry Addition:**
|
||||||
|
```python
|
||||||
|
sketch = ImprovedSketch()
|
||||||
|
point = Point2D(10, 20)
|
||||||
|
line = Line2D(start_point, end_point)
|
||||||
|
circle = Circle2D(center_point, radius)
|
||||||
|
|
||||||
|
sketch.add_point(point)
|
||||||
|
sketch.add_line(line)
|
||||||
|
sketch.add_circle(circle)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Constraint Application:**
|
||||||
|
```python
|
||||||
|
# Distance constraint
|
||||||
|
sketch.distance(point1.handle, point2.handle, 50.0, sketch.wp)
|
||||||
|
|
||||||
|
# Coincident constraint
|
||||||
|
sketch.coincident(point1.handle, point2.handle, sketch.wp)
|
||||||
|
|
||||||
|
# Line constraints
|
||||||
|
sketch.horizontal(line.handle, sketch.wp)
|
||||||
|
sketch.vertical(line.handle, sketch.wp)
|
||||||
|
|
||||||
|
# Solve system
|
||||||
|
result = sketch.solve_system()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Signals
|
||||||
|
|
||||||
|
The widget emits several signals for integration:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Emitted when constraint is successfully applied
|
||||||
|
widget.constraint_applied.connect(callback)
|
||||||
|
|
||||||
|
# Emitted when new geometry is created
|
||||||
|
widget.geometry_created.connect(callback) # Parameter: geometry type string
|
||||||
|
|
||||||
|
# Emitted when sketch is modified
|
||||||
|
widget.sketch_modified.connect(callback)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Optimization Strategies
|
||||||
|
|
||||||
|
1. **Lazy Solving**: Solver only runs when necessary (after constraints or drag end)
|
||||||
|
2. **Efficient Rendering**: Uses Qt's optimized drawing primitives
|
||||||
|
3. **Smart Updates**: Only redraws affected regions when possible
|
||||||
|
4. **Handle Caching**: SolverSpace handles are cached to avoid recreation
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
- Geometry objects use weak references where possible
|
||||||
|
- SolverSpace handles are properly cleaned up
|
||||||
|
- Qt objects follow parent-child hierarchy for automatic cleanup
|
||||||
|
|
||||||
|
### Scalability Limits
|
||||||
|
|
||||||
|
- Recommended maximum: ~1000 geometric entities
|
||||||
|
- Solver performance degrades with complex constraint networks
|
||||||
|
- Rendering remains smooth up to ~10,000 entities
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Solver Failures
|
||||||
|
**Symptoms**: Constraints not applied, geometry not updating
|
||||||
|
**Causes**: Over-constrained systems, conflicting constraints
|
||||||
|
**Solutions**:
|
||||||
|
- Check constraint compatibility
|
||||||
|
- Verify geometry validity
|
||||||
|
- Use `ResultFlag` inspection for error details
|
||||||
|
|
||||||
|
#### Coordinate Transform Issues
|
||||||
|
**Symptoms**: Mouse clicks don't match visual geometry
|
||||||
|
**Causes**: Incorrect transform calculations, zoom/pan state corruption
|
||||||
|
**Solutions**:
|
||||||
|
- Verify `_viewport_to_local` and `_setup_coordinate_system` consistency
|
||||||
|
- Reset view with `zoom_to_fit()`
|
||||||
|
|
||||||
|
#### Performance Problems
|
||||||
|
**Symptoms**: Slow dragging, UI lag
|
||||||
|
**Causes**: Solver running during drag, excessive redraws
|
||||||
|
**Solutions**:
|
||||||
|
- Ensure solver only runs in `_end_point_drag`
|
||||||
|
- Check render loop efficiency
|
||||||
|
- Profile with Qt performance tools
|
||||||
|
|
||||||
|
#### Snap Behavior Issues
|
||||||
|
**Symptoms**: Inconsistent snapping, incorrect snap points
|
||||||
|
**Causes**: Priority conflicts, threshold settings, coordinate errors
|
||||||
|
**Solutions**:
|
||||||
|
- Adjust snap threshold in `SnapSettings`
|
||||||
|
- Verify snap priority order
|
||||||
|
- Check coordinate conversion in snap calculations
|
||||||
|
|
||||||
|
### Debug Logging
|
||||||
|
|
||||||
|
Enable detailed logging for troubleshooting:
|
||||||
|
```python
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logger = logging.getLogger('improved_sketcher')
|
||||||
|
```
|
||||||
|
|
||||||
|
Key log messages include:
|
||||||
|
- Geometry addition/removal
|
||||||
|
- Constraint application results
|
||||||
|
- Solver execution status
|
||||||
|
- Coordinate transformations
|
||||||
|
- Snap calculations
|
||||||
|
|
||||||
|
### Testing Guidelines
|
||||||
|
|
||||||
|
#### Unit Testing
|
||||||
|
- Test geometry classes with edge cases
|
||||||
|
- Verify coordinate transformations
|
||||||
|
- Test constraint application logic
|
||||||
|
|
||||||
|
#### Integration Testing
|
||||||
|
- Test with various sketch sizes
|
||||||
|
- Verify working plane integration
|
||||||
|
- Test complex constraint networks
|
||||||
|
|
||||||
|
#### Performance Testing
|
||||||
|
- Measure solver execution time
|
||||||
|
- Profile rendering performance
|
||||||
|
- Test with large geometry sets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The ImprovedSketchWidget provides a robust, extensible foundation for 2D parametric sketching in Fluency CAD. Its architecture separates concerns effectively, uses proven libraries (SolverSpace, PySide6), and provides rich interaction capabilities while maintaining good performance characteristics.
|
||||||
|
|
||||||
|
The system is designed for extensibility - new geometry types, constraint types, and interaction modes can be added following the established patterns. The comprehensive API allows for both direct use and integration with larger CAD systems.
|
||||||
BIN
drawing_modules/.DS_Store
vendored
Normal file
BIN
drawing_modules/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/draw_widget2d.cpython-311.pyc
Normal file
BIN
drawing_modules/__pycache__/draw_widget2d.cpython-311.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/draw_widget_solve.cpython-311.pyc
Normal file
BIN
drawing_modules/__pycache__/draw_widget_solve.cpython-311.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/draw_widget_solve.cpython-312.pyc
Normal file
BIN
drawing_modules/__pycache__/draw_widget_solve.cpython-312.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/gl_widget.cpython-311.pyc
Normal file
BIN
drawing_modules/__pycache__/gl_widget.cpython-311.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/improved_sketcher.cpython-312.pyc
Normal file
BIN
drawing_modules/__pycache__/improved_sketcher.cpython-312.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/vtk_widget.cpython-311.pyc
Normal file
BIN
drawing_modules/__pycache__/vtk_widget.cpython-311.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/vtk_widget.cpython-312.pyc
Normal file
BIN
drawing_modules/__pycache__/vtk_widget.cpython-312.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/vysta_widget.cpython-311.pyc
Normal file
BIN
drawing_modules/__pycache__/vysta_widget.cpython-311.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/__pycache__/vysta_widget.cpython-312.pyc
Normal file
BIN
drawing_modules/__pycache__/vysta_widget.cpython-312.pyc
Normal file
Binary file not shown.
BIN
drawing_modules/case.stl
Normal file
BIN
drawing_modules/case.stl
Normal file
Binary file not shown.
@@ -900,7 +900,7 @@ class SketchWidget(QWidget):
|
|||||||
pen_planned.setStyle(Qt.PenStyle.DotLine)
|
pen_planned.setStyle(Qt.PenStyle.DotLine)
|
||||||
pen_planned.setWidthF(2 / self.zoom)
|
pen_planned.setWidthF(2 / self.zoom)
|
||||||
|
|
||||||
pen_construct = QPen(Qt.cyan)
|
pen_construct = QPen(Qt.green)
|
||||||
pen_construct.setStyle(Qt.PenStyle.DotLine)
|
pen_construct.setStyle(Qt.PenStyle.DotLine)
|
||||||
pen_construct.setWidthF(1 / self.zoom)
|
pen_construct.setWidthF(1 / self.zoom)
|
||||||
|
|
||||||
|
|||||||
1524
drawing_modules/improved_sketcher.py
Normal file
1524
drawing_modules/improved_sketcher.py
Normal file
File diff suppressed because it is too large
Load Diff
201
drawing_modules/sketcher_integration_example.py
Normal file
201
drawing_modules/sketcher_integration_example.py
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
"""
|
||||||
|
Example integration of the improved sketcher with the main Fluency application
|
||||||
|
This shows how to replace the existing sketcher with the improved version
|
||||||
|
"""
|
||||||
|
|
||||||
|
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QPushButton, QButtonGroup
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
from improved_sketcher import ImprovedSketchWidget, SketchMode, SnapMode
|
||||||
|
|
||||||
|
|
||||||
|
class SketcherIntegrationDemo(QMainWindow):
|
||||||
|
"""Demo showing how to integrate the improved sketcher with UI controls"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.setWindowTitle("Improved Sketcher Integration Demo")
|
||||||
|
self.resize(1200, 800)
|
||||||
|
|
||||||
|
# Create central widget
|
||||||
|
central_widget = QWidget()
|
||||||
|
self.setCentralWidget(central_widget)
|
||||||
|
|
||||||
|
# Create layout
|
||||||
|
main_layout = QHBoxLayout(central_widget)
|
||||||
|
|
||||||
|
# Create toolbar
|
||||||
|
self.create_toolbar(main_layout)
|
||||||
|
|
||||||
|
# Create sketcher widget
|
||||||
|
self.sketcher = ImprovedSketchWidget()
|
||||||
|
main_layout.addWidget(self.sketcher, stretch=1)
|
||||||
|
|
||||||
|
# Connect sketcher signals
|
||||||
|
self.connect_sketcher_signals()
|
||||||
|
|
||||||
|
# Set initial mode
|
||||||
|
self.sketcher.set_mode(SketchMode.LINE)
|
||||||
|
|
||||||
|
def create_toolbar(self, parent_layout):
|
||||||
|
"""Create toolbar with sketching tools"""
|
||||||
|
toolbar_widget = QWidget()
|
||||||
|
toolbar_widget.setFixedWidth(200)
|
||||||
|
toolbar_layout = QVBoxLayout(toolbar_widget)
|
||||||
|
|
||||||
|
# Drawing tools group
|
||||||
|
drawing_group = QWidget()
|
||||||
|
drawing_layout = QVBoxLayout(drawing_group)
|
||||||
|
drawing_layout.addWidget(self.create_label("Drawing Tools"))
|
||||||
|
|
||||||
|
# Create drawing mode buttons
|
||||||
|
self.drawing_buttons = QButtonGroup(self)
|
||||||
|
self.drawing_buttons.setExclusive(True)
|
||||||
|
|
||||||
|
drawing_modes = [
|
||||||
|
("Line", SketchMode.LINE),
|
||||||
|
("Rectangle", SketchMode.RECTANGLE),
|
||||||
|
("Circle", SketchMode.CIRCLE),
|
||||||
|
("Point", SketchMode.POINT),
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, mode in drawing_modes:
|
||||||
|
button = QPushButton(name)
|
||||||
|
button.setCheckable(True)
|
||||||
|
button.clicked.connect(lambda checked, m=mode: self.set_drawing_mode(m))
|
||||||
|
self.drawing_buttons.addButton(button)
|
||||||
|
drawing_layout.addWidget(button)
|
||||||
|
|
||||||
|
# Set line as default
|
||||||
|
self.drawing_buttons.buttons()[0].setChecked(True)
|
||||||
|
|
||||||
|
# Constraint tools group
|
||||||
|
constraint_group = QWidget()
|
||||||
|
constraint_layout = QVBoxLayout(constraint_group)
|
||||||
|
constraint_layout.addWidget(self.create_label("Constraints"))
|
||||||
|
|
||||||
|
# Create constraint buttons
|
||||||
|
constraint_modes = [
|
||||||
|
("Coincident", SketchMode.COINCIDENT_PT_PT),
|
||||||
|
("Horizontal", SketchMode.HORIZONTAL),
|
||||||
|
("Vertical", SketchMode.VERTICAL),
|
||||||
|
("Distance", SketchMode.DISTANCE),
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, mode in constraint_modes:
|
||||||
|
button = QPushButton(name)
|
||||||
|
button.clicked.connect(lambda checked, m=mode: self.set_constraint_mode(m))
|
||||||
|
constraint_layout.addWidget(button)
|
||||||
|
|
||||||
|
# Settings group
|
||||||
|
settings_group = QWidget()
|
||||||
|
settings_layout = QVBoxLayout(settings_group)
|
||||||
|
settings_layout.addWidget(self.create_label("Settings"))
|
||||||
|
|
||||||
|
# Construction mode toggle
|
||||||
|
self.construction_button = QPushButton("Construction Mode")
|
||||||
|
self.construction_button.setCheckable(True)
|
||||||
|
self.construction_button.toggled.connect(self.toggle_construction_mode)
|
||||||
|
settings_layout.addWidget(self.construction_button)
|
||||||
|
|
||||||
|
# Snap settings
|
||||||
|
snap_buttons = [
|
||||||
|
("Point Snap", SnapMode.POINT),
|
||||||
|
("Grid Snap", SnapMode.GRID),
|
||||||
|
("Midpoint Snap", SnapMode.MIDPOINT),
|
||||||
|
]
|
||||||
|
|
||||||
|
for name, snap_mode in snap_buttons:
|
||||||
|
button = QPushButton(name)
|
||||||
|
button.setCheckable(True)
|
||||||
|
button.toggled.connect(lambda checked, sm=snap_mode: self.toggle_snap_mode(sm, checked))
|
||||||
|
settings_layout.addWidget(button)
|
||||||
|
|
||||||
|
# Set default snaps
|
||||||
|
settings_layout.itemAt(1).widget().setChecked(True) # Point snap on by default
|
||||||
|
|
||||||
|
# View controls
|
||||||
|
view_group = QWidget()
|
||||||
|
view_layout = QVBoxLayout(view_group)
|
||||||
|
view_layout.addWidget(self.create_label("View"))
|
||||||
|
|
||||||
|
zoom_fit_button = QPushButton("Zoom to Fit")
|
||||||
|
zoom_fit_button.clicked.connect(self.sketcher.zoom_to_fit)
|
||||||
|
view_layout.addWidget(zoom_fit_button)
|
||||||
|
|
||||||
|
# Add groups to toolbar
|
||||||
|
toolbar_layout.addWidget(drawing_group)
|
||||||
|
toolbar_layout.addWidget(constraint_group)
|
||||||
|
toolbar_layout.addWidget(settings_group)
|
||||||
|
toolbar_layout.addWidget(view_group)
|
||||||
|
toolbar_layout.addStretch()
|
||||||
|
|
||||||
|
parent_layout.addWidget(toolbar_widget)
|
||||||
|
|
||||||
|
def create_label(self, text):
|
||||||
|
"""Create a section label"""
|
||||||
|
from PySide6.QtWidgets import QLabel
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
label = QLabel(text)
|
||||||
|
label.setAlignment(Qt.AlignCenter)
|
||||||
|
label.setStyleSheet("font-weight: bold; padding: 5px; background-color: #333; color: white;")
|
||||||
|
return label
|
||||||
|
|
||||||
|
def set_drawing_mode(self, mode):
|
||||||
|
"""Set the sketcher to drawing mode"""
|
||||||
|
self.sketcher.set_mode(mode)
|
||||||
|
print(f"Drawing mode set to: {mode.name}")
|
||||||
|
|
||||||
|
def set_constraint_mode(self, mode):
|
||||||
|
"""Set the sketcher to constraint mode"""
|
||||||
|
self.sketcher.set_mode(mode)
|
||||||
|
# Uncheck all drawing buttons when in constraint mode
|
||||||
|
for button in self.drawing_buttons.buttons():
|
||||||
|
button.setChecked(False)
|
||||||
|
print(f"Constraint mode set to: {mode.name}")
|
||||||
|
|
||||||
|
def toggle_construction_mode(self, checked):
|
||||||
|
"""Toggle construction geometry mode"""
|
||||||
|
self.sketcher.set_construction_mode(checked)
|
||||||
|
print(f"Construction mode: {'enabled' if checked else 'disabled'}")
|
||||||
|
|
||||||
|
def toggle_snap_mode(self, snap_mode, enabled):
|
||||||
|
"""Toggle snap mode"""
|
||||||
|
self.sketcher.toggle_snap_mode(snap_mode, enabled)
|
||||||
|
print(f"Snap mode {snap_mode.name}: {'enabled' if enabled else 'disabled'}")
|
||||||
|
|
||||||
|
def connect_sketcher_signals(self):
|
||||||
|
"""Connect to sketcher signals for feedback"""
|
||||||
|
self.sketcher.geometry_created.connect(self.on_geometry_created)
|
||||||
|
self.sketcher.constraint_applied.connect(self.on_constraint_applied)
|
||||||
|
self.sketcher.sketch_modified.connect(self.on_sketch_modified)
|
||||||
|
|
||||||
|
def on_geometry_created(self, geometry_type):
|
||||||
|
"""Handle geometry creation"""
|
||||||
|
print(f"Created: {geometry_type}")
|
||||||
|
# Update status or trigger other actions
|
||||||
|
|
||||||
|
def on_constraint_applied(self):
|
||||||
|
"""Handle constraint application"""
|
||||||
|
print("Constraint applied successfully")
|
||||||
|
# Return to line drawing mode after constraint
|
||||||
|
self.sketcher.set_mode(SketchMode.LINE)
|
||||||
|
self.drawing_buttons.buttons()[0].setChecked(True)
|
||||||
|
|
||||||
|
def on_sketch_modified(self):
|
||||||
|
"""Handle sketch modifications"""
|
||||||
|
print("Sketch modified")
|
||||||
|
# Could trigger auto-save or update displays
|
||||||
|
|
||||||
|
|
||||||
|
def replace_sketcher_in_main_app():
|
||||||
|
"""
|
||||||
|
Example of how to replace the existing sketcher in main.py
|
||||||
|
|
||||||
|
In main.py, replace this code:
|
||||||
|
|
||||||
|
```python\n from drawing_modules.draw_widget_solve import SketchWidget\n self.sketchWidget = SketchWidget()\n ```\n \n With:\n \n ```python\n from drawing_modules.improved_sketcher import ImprovedSketchWidget, SketchMode\n self.sketchWidget = ImprovedSketchWidget()\n \n # Connect to existing signals (adapt as needed)\n self.sketchWidget.constraint_applied.connect(self.draw_op_complete)\n self.sketchWidget.sketch_modified.connect(self.on_sketch_changed)\n \n # Connect toolbar buttons to new sketcher modes\n self.ui.pb_linetool.clicked.connect(lambda: self.sketchWidget.set_mode(SketchMode.LINE))\n self.ui.pb_rectool.clicked.connect(lambda: self.sketchWidget.set_mode(SketchMode.RECTANGLE))\n # ... etc for other buttons\n ```\n \n The improved sketcher provides these advantages:\n \n 1. **Better Architecture**: Clean separation of concerns, proper error handling\n 2. **Enhanced Features**: Rectangle and circle tools, improved constraints\n 3. **Better Performance**: Optimized rendering and interaction handling\n 4. **Extensibility**: Easy to add new tools and constraints\n 5. **Type Safety**: Proper type hints and validation\n 6. **Logging**: Built-in logging for debugging\n 7. **Settings**: Configurable snap and render settings\n \n Key differences to adapt:\n \n - Use SketchMode enum instead of string modes\n - Connect to new signal names (constraint_applied, geometry_created, sketch_modified)\n - Use set_mode() instead of individual mode methods\n - Access sketch data through self.sketch property\n - Use new geometry classes (Point2D, Line2D, Circle2D)\n """\n pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":\n import sys\n \n app = QApplication(sys.argv)\n \n # Create and show the integration demo\n demo = SketcherIntegrationDemo()\n demo.show()\n \n print("Improved Sketcher Integration Demo")\n print("==================================")\n print("Features:")\n print("- Line, Rectangle, Circle, Point drawing")\n print("- Coincident, Horizontal, Vertical, Distance constraints")\n print("- Construction geometry mode")\n print("- Point, Grid, Midpoint snapping")\n print("- Zoom to fit")\n print("- Mouse wheel zoom")\n print("- Right-click to cancel operations")\n print("")\n print("Usage:")\n print("- Select a drawing tool and click in the viewport")\n print("- Right-click to finish multi-point operations")\n print("- Use constraint tools to add relationships")\n print("- Toggle construction mode for helper geometry")\n \n sys.exit(app.exec())
|
||||||
22
main.app/Contents/Info.plist
Normal file
22
main.app/Contents/Info.plist
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>main</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>main</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>main</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>main</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
BIN
main.app/Contents/MacOS/PIL/_imaging.so
Normal file
BIN
main.app/Contents/MacOS/PIL/_imaging.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PIL/_imagingcms.so
Normal file
BIN
main.app/Contents/MacOS/PIL/_imagingcms.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PIL/_imagingft.so
Normal file
BIN
main.app/Contents/MacOS/PIL/_imagingft.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PIL/_webp.so
Normal file
BIN
main.app/Contents/MacOS/PIL/_webp.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtCore.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtCore.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtDataVisualization.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtDataVisualization.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtGui.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtGui.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtOpenGL.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtOpenGL.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtOpenGLWidgets.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtOpenGLWidgets.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/PySide6/QtWidgets.so
Normal file
BIN
main.app/Contents/MacOS/PySide6/QtWidgets.so
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
main.app/Contents/MacOS/Python
Normal file
BIN
main.app/Contents/MacOS/Python
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtCore
Normal file
BIN
main.app/Contents/MacOS/QtCore
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtDBus
Normal file
BIN
main.app/Contents/MacOS/QtDBus
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtDataVisualization
Normal file
BIN
main.app/Contents/MacOS/QtDataVisualization
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtGui
Normal file
BIN
main.app/Contents/MacOS/QtGui
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtNetwork
Normal file
BIN
main.app/Contents/MacOS/QtNetwork
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtOpenGL
Normal file
BIN
main.app/Contents/MacOS/QtOpenGL
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtOpenGLWidgets
Normal file
BIN
main.app/Contents/MacOS/QtOpenGLWidgets
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtPdf
Normal file
BIN
main.app/Contents/MacOS/QtPdf
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtSvg
Normal file
BIN
main.app/Contents/MacOS/QtSvg
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/QtWidgets
Normal file
BIN
main.app/Contents/MacOS/QtWidgets
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_asyncio.so
Normal file
BIN
main.app/Contents/MacOS/_asyncio.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_blake2.so
Normal file
BIN
main.app/Contents/MacOS/_blake2.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_bz2.so
Normal file
BIN
main.app/Contents/MacOS/_bz2.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_cffi_backend.so
Normal file
BIN
main.app/Contents/MacOS/_cffi_backend.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_cn.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_cn.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_hk.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_hk.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_iso2022.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_iso2022.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_jp.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_jp.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_kr.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_kr.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_codecs_tw.so
Normal file
BIN
main.app/Contents/MacOS/_codecs_tw.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_contextvars.so
Normal file
BIN
main.app/Contents/MacOS/_contextvars.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_csv.so
Normal file
BIN
main.app/Contents/MacOS/_csv.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_ctypes.so
Normal file
BIN
main.app/Contents/MacOS/_ctypes.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_datetime.so
Normal file
BIN
main.app/Contents/MacOS/_datetime.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_decimal.so
Normal file
BIN
main.app/Contents/MacOS/_decimal.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_elementtree.so
Normal file
BIN
main.app/Contents/MacOS/_elementtree.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_hashlib.so
Normal file
BIN
main.app/Contents/MacOS/_hashlib.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_heapq.so
Normal file
BIN
main.app/Contents/MacOS/_heapq.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_lzma.so
Normal file
BIN
main.app/Contents/MacOS/_lzma.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_md5.so
Normal file
BIN
main.app/Contents/MacOS/_md5.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_multibytecodec.so
Normal file
BIN
main.app/Contents/MacOS/_multibytecodec.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_multiprocessing.so
Normal file
BIN
main.app/Contents/MacOS/_multiprocessing.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_opcode.so
Normal file
BIN
main.app/Contents/MacOS/_opcode.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_pickle.so
Normal file
BIN
main.app/Contents/MacOS/_pickle.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_posixshmem.so
Normal file
BIN
main.app/Contents/MacOS/_posixshmem.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_posixsubprocess.so
Normal file
BIN
main.app/Contents/MacOS/_posixsubprocess.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_queue.so
Normal file
BIN
main.app/Contents/MacOS/_queue.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_random.so
Normal file
BIN
main.app/Contents/MacOS/_random.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_scproxy.so
Normal file
BIN
main.app/Contents/MacOS/_scproxy.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_sha1.so
Normal file
BIN
main.app/Contents/MacOS/_sha1.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_sha256.so
Normal file
BIN
main.app/Contents/MacOS/_sha256.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_sha3.so
Normal file
BIN
main.app/Contents/MacOS/_sha3.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_sha512.so
Normal file
BIN
main.app/Contents/MacOS/_sha512.so
Normal file
Binary file not shown.
BIN
main.app/Contents/MacOS/_socket.so
Normal file
BIN
main.app/Contents/MacOS/_socket.so
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user