Improve datum behaviour
This commit is contained in:
198
PLAN.md
Normal file
198
PLAN.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# ZTools Development Plan
|
||||
|
||||
## Current Status: v0.1.0 (70% complete)
|
||||
|
||||
### What's Working
|
||||
- Workbench registration with 10 toolbars and menus
|
||||
- All 15 datum creation functions with custom ZTools attachment system
|
||||
- Datum Creator GUI (task panel with Planes/Axes/Points tabs)
|
||||
- OK button creates datum, Cancel dismisses without creating
|
||||
- Rotated Linear Pattern feature (complete)
|
||||
- Icon system (21+ Catppuccin-themed SVGs)
|
||||
- Metadata storage system (ZTools_Type, ZTools_Params, ZTools_SourceRefs)
|
||||
- Spreadsheet linking for parametric control
|
||||
|
||||
### Recent Changes (2026-01-24)
|
||||
- Replaced FreeCAD's vanilla attachment system with custom ZTools attachment
|
||||
- All datums now use `MapMode='Deactivated'` with calculated placements
|
||||
- Source references stored in `ZTools_SourceRefs` property for future update capability
|
||||
- Fixed all 3 point functions (`point_on_edge`, `point_center_of_face`, `point_center_of_circle`) to accept source parameters
|
||||
- Removed redundant "Create Datum" button - OK now creates the datum
|
||||
- Task panel properly cleans up selection observer on close
|
||||
|
||||
---
|
||||
|
||||
## ZTools Attachment System
|
||||
|
||||
FreeCAD's vanilla attachment system has reliability issues. ZTools uses a custom approach:
|
||||
|
||||
1. **Calculate placement directly** from source geometry at creation time
|
||||
2. **Store source references** in `ZTools_SourceRefs` property (JSON)
|
||||
3. **Use `MapMode='Deactivated'`** to prevent FreeCAD attachment interference
|
||||
4. **Store creation parameters** in `ZTools_Params` for potential recalculation
|
||||
|
||||
This gives full control over datum positioning while maintaining the ability to update datums when source geometry changes (future feature).
|
||||
|
||||
### Metadata Properties
|
||||
|
||||
All ZTools datums have these custom properties:
|
||||
- `ZTools_Type`: Creation method identifier (e.g., "offset_from_face", "midplane")
|
||||
- `ZTools_Params`: JSON-encoded creation parameters
|
||||
- `ZTools_SourceRefs`: JSON-encoded list of source geometry references
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Complete (Datum Tools)
|
||||
|
||||
All datum creation functions now work:
|
||||
|
||||
### Planes (6 modes)
|
||||
- Offset from Face
|
||||
- Midplane (2 Faces)
|
||||
- 3 Points
|
||||
- Normal to Edge
|
||||
- Angled from Face
|
||||
- Tangent to Cylinder
|
||||
|
||||
### Axes (4 modes)
|
||||
- 2 Points
|
||||
- From Edge
|
||||
- Cylinder Center
|
||||
- Plane Intersection
|
||||
|
||||
### Points (5 modes)
|
||||
- At Vertex
|
||||
- XYZ Coordinates
|
||||
- On Edge (with parameter)
|
||||
- Face Center
|
||||
- Circle Center
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Complete Enhanced Pocket
|
||||
|
||||
### 2.1 Wire Up Pocket Execution (pocket_commands.py)
|
||||
|
||||
The EnhancedPocketTaskPanel has complete UI but no execute logic.
|
||||
|
||||
Required implementation:
|
||||
1. Get selected sketch from user
|
||||
2. Create PartDesign::Pocket with selected type
|
||||
3. Apply "Flip Side to Cut" by:
|
||||
- Reversing the pocket direction, OR
|
||||
- Using a boolean cut approach with inverted profile
|
||||
4. Handle all pocket types: Dimension, Through All, To First, Up To Face, Two Dimensions
|
||||
|
||||
### 2.2 Register Pocket Command
|
||||
|
||||
Add to InitGui.py toolbar if not already present.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Datum Manager
|
||||
|
||||
### 3.1 Implement DatumManagerTaskPanel
|
||||
|
||||
Replace the stub in datum_commands.py with functional panel:
|
||||
|
||||
Features:
|
||||
- List all datum objects (planes, axes, points) in document
|
||||
- Filter by type (ZTools-created vs native)
|
||||
- Toggle visibility (eye icon per item)
|
||||
- Rename datums inline
|
||||
- Delete selected datums
|
||||
- Jump to datum in model tree
|
||||
|
||||
UI Layout:
|
||||
```
|
||||
+----------------------------------+
|
||||
| Filter: [All v] [ZTools only ☐] |
|
||||
+----------------------------------+
|
||||
| ☑ ZPlane_Offset_001 [👁] [🗑] |
|
||||
| ☑ ZPlane_Mid_001 [👁] [🗑] |
|
||||
| ☐ ZAxis_Cyl_001 [👁] [🗑] |
|
||||
+----------------------------------+
|
||||
| [Rename] [Show All] [Hide All] |
|
||||
+----------------------------------+
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Additional Features (Future)
|
||||
|
||||
### 4.1 Module 2 Completion: Enhanced Pad
|
||||
- Multi-body support
|
||||
- Draft angles on pad
|
||||
- Lip/groove profiles
|
||||
|
||||
### 4.2 Module 3: Body Operations
|
||||
- Split body at plane
|
||||
- Combine bodies
|
||||
- Shell improvements
|
||||
|
||||
### 4.3 Module 4: Pattern Tools
|
||||
- Curve-driven pattern (sweep instances along spline)
|
||||
- Fill pattern (populate region with instances)
|
||||
- Pattern with variable spacing
|
||||
|
||||
### 4.4 Datum Update Feature
|
||||
- Use stored `ZTools_SourceRefs` to recalculate datum positions
|
||||
- Handle topology changes gracefully
|
||||
- Option to "freeze" datums (disconnect from sources)
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
|
||||
| File | Purpose | Lines |
|
||||
|------|---------|-------|
|
||||
| `ztools/ztools/datums/core.py` | Datum creation functions | ~750 |
|
||||
| `ztools/ztools/commands/datum_commands.py` | Datum Creator/Manager GUI | ~520 |
|
||||
| `ztools/ztools/commands/pocket_commands.py` | Enhanced Pocket GUI | ~600 |
|
||||
| `ztools/ztools/commands/pattern_commands.py` | Rotated Linear Pattern | ~206 |
|
||||
| `ztools/InitGui.py` | Workbench registration | ~200 |
|
||||
| `ztools/ztools/resources/icons.py` | SVG icon definitions | ~400 |
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Phase 1 Tests (Datum Tools)
|
||||
- [ ] Create plane offset from face
|
||||
- [ ] Create midplane between 2 faces
|
||||
- [ ] Create plane from 3 points
|
||||
- [ ] Create plane normal to edge at various parameters
|
||||
- [ ] Create angled plane from face about edge
|
||||
- [ ] Create plane tangent to cylinder
|
||||
- [ ] Create axis from 2 points
|
||||
- [ ] Create axis from edge
|
||||
- [ ] Create axis at cylinder center
|
||||
- [ ] Create axis at plane intersection
|
||||
- [ ] Create point at vertex
|
||||
- [ ] Create point at XYZ coordinates
|
||||
- [ ] Create point on edge at parameter 0.0, 0.5, 1.0
|
||||
- [ ] Create point at face center (planar and cylindrical)
|
||||
- [ ] Create point at circle center (full circle and arc)
|
||||
- [ ] Verify ZTools_Type, ZTools_Params, ZTools_SourceRefs properties exist
|
||||
- [ ] Verify no "deactivated attachment mode" warnings in console
|
||||
|
||||
### Phase 2 Tests (Enhanced Pocket)
|
||||
- [ ] Create pocket with Dimension type
|
||||
- [ ] Create pocket with Through All
|
||||
- [ ] Create pocket with Flip Side to Cut enabled
|
||||
- [ ] Verify pocket respects taper angle
|
||||
|
||||
### Phase 3 Tests (Datum Manager)
|
||||
- [ ] Datum Manager lists all datums
|
||||
- [ ] Visibility toggle works
|
||||
- [ ] Rename persists after recompute
|
||||
- [ ] Delete removes datum cleanly
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- FreeCAD 1.0+ required (TNP mitigation assumed)
|
||||
- ZTools uses custom attachment system (not FreeCAD's vanilla attachment)
|
||||
- Catppuccin Mocha theme is bundled as preference pack
|
||||
- LGPL-3.0-or-later license
|
||||
@@ -1,4 +1,9 @@
|
||||
# ztools/commands - GUI commands
|
||||
from . import datum_commands, pattern_commands, pocket_commands
|
||||
from . import datum_commands, datum_viewprovider, pattern_commands, pocket_commands
|
||||
|
||||
__all__ = ["datum_commands", "pattern_commands", "pocket_commands"]
|
||||
__all__ = [
|
||||
"datum_commands",
|
||||
"datum_viewprovider",
|
||||
"pattern_commands",
|
||||
"pocket_commands",
|
||||
]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
405
ztools/ztools/commands/datum_viewprovider.py
Normal file
405
ztools/ztools/commands/datum_viewprovider.py
Normal file
@@ -0,0 +1,405 @@
|
||||
# ztools/commands/datum_viewprovider.py
|
||||
# Custom ViewProvider for ZTools datum objects
|
||||
|
||||
import json
|
||||
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
import Part
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
|
||||
class ZToolsDatumViewProvider:
|
||||
"""
|
||||
Custom ViewProvider for ZTools datum objects.
|
||||
|
||||
Features:
|
||||
- Overrides double-click to open ZTools editor instead of vanilla attachment
|
||||
- Hides attachment properties from property editor
|
||||
- Provides custom icons based on datum type
|
||||
"""
|
||||
|
||||
_is_ztools = True # Marker to identify ZTools ViewProviders
|
||||
|
||||
def __init__(self, vobj):
|
||||
"""Initialize and attach to ViewObject."""
|
||||
vobj.Proxy = self
|
||||
self.Object = vobj.Object if vobj else None
|
||||
|
||||
def attach(self, vobj):
|
||||
"""Called when ViewProvider is attached to object."""
|
||||
self.Object = vobj.Object
|
||||
self._hide_attachment_props(vobj)
|
||||
|
||||
def _hide_attachment_props(self, vobj):
|
||||
"""Hide FreeCAD attachment properties."""
|
||||
if not vobj or not vobj.Object:
|
||||
return
|
||||
|
||||
obj = vobj.Object
|
||||
attachment_props = [
|
||||
"MapMode",
|
||||
"MapPathParameter",
|
||||
"MapReversed",
|
||||
"AttachmentOffset",
|
||||
"Support",
|
||||
]
|
||||
|
||||
for prop in attachment_props:
|
||||
try:
|
||||
if hasattr(obj, prop):
|
||||
vobj.setEditorMode(prop, 2) # 2 = Hidden
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def updateData(self, obj, prop):
|
||||
"""Called when data properties change."""
|
||||
pass
|
||||
|
||||
def onChanged(self, vobj, prop):
|
||||
"""Called when view properties change."""
|
||||
# Re-hide attachment properties if they become visible
|
||||
if prop in ("MapMode", "Support"):
|
||||
self._hide_attachment_props(vobj)
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
"""
|
||||
Handle double-click - open ZTools datum editor.
|
||||
Returns True if handled, False to let FreeCAD handle it.
|
||||
"""
|
||||
if Gui.Control.activeDialog():
|
||||
# Task panel already open
|
||||
return False
|
||||
|
||||
# Check if this is a ZTools datum
|
||||
obj = vobj.Object
|
||||
if not hasattr(obj, "ZTools_Type"):
|
||||
# Not a ZTools datum, let FreeCAD handle it
|
||||
return False
|
||||
|
||||
# Open ZTools editor
|
||||
panel = DatumEditTaskPanel(obj)
|
||||
Gui.Control.showDialog(panel)
|
||||
return True
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
"""
|
||||
Called when entering edit mode.
|
||||
Mode 0 = default edit, Mode 1 = transform
|
||||
"""
|
||||
if mode == 0:
|
||||
obj = vobj.Object
|
||||
if hasattr(obj, "ZTools_Type"):
|
||||
panel = DatumEditTaskPanel(obj)
|
||||
Gui.Control.showDialog(panel)
|
||||
return True
|
||||
return False
|
||||
|
||||
def unsetEdit(self, vobj, mode=0):
|
||||
"""Called when exiting edit mode."""
|
||||
return False
|
||||
|
||||
def getIcon(self):
|
||||
"""Return icon for tree view based on datum type."""
|
||||
from ztools.resources.icons import get_icon
|
||||
|
||||
if not self.Object:
|
||||
return get_icon("datum_creator")
|
||||
|
||||
ztools_type = getattr(self.Object, "ZTools_Type", "")
|
||||
|
||||
# Map ZTools type to icon
|
||||
icon_map = {
|
||||
"offset_from_face": "plane_offset",
|
||||
"offset_from_plane": "plane_offset",
|
||||
"midplane": "plane_midplane",
|
||||
"3_points": "plane_3pt",
|
||||
"normal_to_edge": "plane_normal",
|
||||
"angled": "plane_angled",
|
||||
"tangent_cylinder": "plane_tangent",
|
||||
"2_points": "axis_2pt",
|
||||
"from_edge": "axis_edge",
|
||||
"cylinder_center": "axis_cyl",
|
||||
"plane_intersection": "axis_intersect",
|
||||
"vertex": "point_vertex",
|
||||
"coordinates": "point_xyz",
|
||||
"on_edge": "point_edge",
|
||||
"face_center": "point_face",
|
||||
"circle_center": "point_circle",
|
||||
}
|
||||
|
||||
icon_name = icon_map.get(ztools_type, "datum_creator")
|
||||
return get_icon(icon_name)
|
||||
|
||||
def __getstate__(self):
|
||||
"""Serialization - don't save proxy state."""
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
"""Deserialization."""
|
||||
return None
|
||||
|
||||
|
||||
class DatumEditTaskPanel:
|
||||
"""
|
||||
Task panel for editing existing ZTools datum objects.
|
||||
|
||||
Allows modification of:
|
||||
- Offset distance (for offset-type datums)
|
||||
- Angle (for angled/tangent datums)
|
||||
- Parameter position (for edge-based datums)
|
||||
- Source references (future)
|
||||
"""
|
||||
|
||||
def __init__(self, datum_obj):
|
||||
self.datum_obj = datum_obj
|
||||
self.form = QtGui.QWidget()
|
||||
self.form.setWindowTitle(f"Edit {datum_obj.Label}")
|
||||
self.original_placement = datum_obj.Placement.copy()
|
||||
self.setup_ui()
|
||||
self.load_current_values()
|
||||
|
||||
def setup_ui(self):
|
||||
"""Create the edit panel UI."""
|
||||
layout = QtGui.QVBoxLayout(self.form)
|
||||
layout.setSpacing(8)
|
||||
|
||||
# Header with datum info
|
||||
info_group = QtGui.QGroupBox("Datum Info")
|
||||
info_layout = QtGui.QFormLayout(info_group)
|
||||
|
||||
self.name_edit = QtGui.QLineEdit(self.datum_obj.Label)
|
||||
info_layout.addRow("Name:", self.name_edit)
|
||||
|
||||
ztools_type = getattr(self.datum_obj, "ZTools_Type", "unknown")
|
||||
type_label = QtGui.QLabel(self._format_type_name(ztools_type))
|
||||
type_label.setStyleSheet("color: #cba6f7; font-weight: bold;")
|
||||
info_layout.addRow("Type:", type_label)
|
||||
|
||||
layout.addWidget(info_group)
|
||||
|
||||
# Parameters group
|
||||
self.params_group = QtGui.QGroupBox("Parameters")
|
||||
self.params_layout = QtGui.QFormLayout(self.params_group)
|
||||
|
||||
# Offset spinner
|
||||
self.offset_spin = QtGui.QDoubleSpinBox()
|
||||
self.offset_spin.setRange(-10000, 10000)
|
||||
self.offset_spin.setSuffix(" mm")
|
||||
self.offset_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
# Angle spinner
|
||||
self.angle_spin = QtGui.QDoubleSpinBox()
|
||||
self.angle_spin.setRange(-360, 360)
|
||||
self.angle_spin.setSuffix(" °")
|
||||
self.angle_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
# Parameter spinner (0-1)
|
||||
self.param_spin = QtGui.QDoubleSpinBox()
|
||||
self.param_spin.setRange(0, 1)
|
||||
self.param_spin.setSingleStep(0.1)
|
||||
self.param_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
# XYZ spinners for point coordinates
|
||||
self.x_spin = QtGui.QDoubleSpinBox()
|
||||
self.x_spin.setRange(-10000, 10000)
|
||||
self.x_spin.setSuffix(" mm")
|
||||
self.x_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
self.y_spin = QtGui.QDoubleSpinBox()
|
||||
self.y_spin.setRange(-10000, 10000)
|
||||
self.y_spin.setSuffix(" mm")
|
||||
self.y_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
self.z_spin = QtGui.QDoubleSpinBox()
|
||||
self.z_spin.setRange(-10000, 10000)
|
||||
self.z_spin.setSuffix(" mm")
|
||||
self.z_spin.valueChanged.connect(self.on_param_changed)
|
||||
|
||||
layout.addWidget(self.params_group)
|
||||
|
||||
# Source references (read-only for now)
|
||||
refs_group = QtGui.QGroupBox("Source References")
|
||||
refs_layout = QtGui.QVBoxLayout(refs_group)
|
||||
|
||||
self.refs_list = QtGui.QListWidget()
|
||||
self.refs_list.setMaximumHeight(80)
|
||||
refs_layout.addWidget(self.refs_list)
|
||||
|
||||
layout.addWidget(refs_group)
|
||||
|
||||
# Placement info (read-only)
|
||||
placement_group = QtGui.QGroupBox("Current Placement")
|
||||
placement_layout = QtGui.QFormLayout(placement_group)
|
||||
|
||||
pos = self.datum_obj.Placement.Base
|
||||
self.pos_label = QtGui.QLabel(f"({pos.x:.2f}, {pos.y:.2f}, {pos.z:.2f})")
|
||||
placement_layout.addRow("Position:", self.pos_label)
|
||||
|
||||
layout.addWidget(placement_group)
|
||||
|
||||
layout.addStretch()
|
||||
|
||||
def _format_type_name(self, ztools_type):
|
||||
"""Format ZTools type string for display."""
|
||||
type_names = {
|
||||
"offset_from_face": "Offset from Face",
|
||||
"offset_from_plane": "Offset from Plane",
|
||||
"midplane": "Midplane",
|
||||
"3_points": "3 Points",
|
||||
"normal_to_edge": "Normal to Edge",
|
||||
"angled": "Angled from Face",
|
||||
"tangent_cylinder": "Tangent to Cylinder",
|
||||
"2_points": "2 Points",
|
||||
"from_edge": "From Edge",
|
||||
"cylinder_center": "Cylinder Center",
|
||||
"plane_intersection": "Plane Intersection",
|
||||
"vertex": "At Vertex",
|
||||
"coordinates": "XYZ Coordinates",
|
||||
"on_edge": "On Edge",
|
||||
"face_center": "Face Center",
|
||||
"circle_center": "Circle Center",
|
||||
}
|
||||
return type_names.get(ztools_type, ztools_type)
|
||||
|
||||
def load_current_values(self):
|
||||
"""Load current values from datum object."""
|
||||
ztools_type = getattr(self.datum_obj, "ZTools_Type", "")
|
||||
params_json = getattr(self.datum_obj, "ZTools_Params", "{}")
|
||||
refs_json = getattr(self.datum_obj, "ZTools_SourceRefs", "[]")
|
||||
|
||||
try:
|
||||
params = json.loads(params_json)
|
||||
except json.JSONDecodeError:
|
||||
params = {}
|
||||
|
||||
try:
|
||||
refs = json.loads(refs_json)
|
||||
except json.JSONDecodeError:
|
||||
refs = []
|
||||
|
||||
# Clear params layout
|
||||
while self.params_layout.rowCount() > 0:
|
||||
self.params_layout.removeRow(0)
|
||||
|
||||
# Add appropriate parameter controls based on type
|
||||
if ztools_type in ("offset_from_face", "offset_from_plane"):
|
||||
distance = params.get("distance", 10)
|
||||
self.offset_spin.setValue(distance)
|
||||
self.params_layout.addRow("Offset:", self.offset_spin)
|
||||
|
||||
elif ztools_type == "angled":
|
||||
angle = params.get("angle", 45)
|
||||
self.angle_spin.setValue(angle)
|
||||
self.params_layout.addRow("Angle:", self.angle_spin)
|
||||
|
||||
elif ztools_type == "tangent_cylinder":
|
||||
angle = params.get("angle", 0)
|
||||
self.angle_spin.setValue(angle)
|
||||
self.params_layout.addRow("Angle:", self.angle_spin)
|
||||
|
||||
elif ztools_type in ("normal_to_edge", "on_edge"):
|
||||
parameter = params.get("parameter", 0.5)
|
||||
self.param_spin.setValue(parameter)
|
||||
self.params_layout.addRow("Position (0-1):", self.param_spin)
|
||||
|
||||
elif ztools_type == "coordinates":
|
||||
x = params.get("x", 0)
|
||||
y = params.get("y", 0)
|
||||
z = params.get("z", 0)
|
||||
self.x_spin.setValue(x)
|
||||
self.y_spin.setValue(y)
|
||||
self.z_spin.setValue(z)
|
||||
self.params_layout.addRow("X:", self.x_spin)
|
||||
self.params_layout.addRow("Y:", self.y_spin)
|
||||
self.params_layout.addRow("Z:", self.z_spin)
|
||||
else:
|
||||
# No editable parameters
|
||||
no_params_label = QtGui.QLabel("No editable parameters")
|
||||
no_params_label.setStyleSheet("color: #888;")
|
||||
self.params_layout.addRow(no_params_label)
|
||||
|
||||
# Load source references
|
||||
self.refs_list.clear()
|
||||
for ref in refs:
|
||||
obj_name = ref.get("object", "?")
|
||||
subname = ref.get("subname", "")
|
||||
if subname:
|
||||
self.refs_list.addItem(f"{obj_name}.{subname}")
|
||||
else:
|
||||
self.refs_list.addItem(obj_name)
|
||||
|
||||
if not refs:
|
||||
self.refs_list.addItem("(no references)")
|
||||
|
||||
def on_param_changed(self):
|
||||
"""Handle parameter value changes - update datum in real-time."""
|
||||
ztools_type = getattr(self.datum_obj, "ZTools_Type", "")
|
||||
|
||||
# For coordinate-based points, update placement directly
|
||||
if ztools_type == "coordinates":
|
||||
new_pos = App.Vector(
|
||||
self.x_spin.value(), self.y_spin.value(), self.z_spin.value()
|
||||
)
|
||||
self.datum_obj.Placement.Base = new_pos
|
||||
self._update_params({"x": new_pos.x, "y": new_pos.y, "z": new_pos.z})
|
||||
|
||||
elif ztools_type in ("offset_from_face", "offset_from_plane"):
|
||||
# For offset types, we need to recalculate from source
|
||||
# This is more complex - for now just update the stored param
|
||||
self._update_params({"distance": self.offset_spin.value()})
|
||||
# TODO: Recalculate placement from source geometry
|
||||
|
||||
elif ztools_type in ("angled", "tangent_cylinder"):
|
||||
self._update_params({"angle": self.angle_spin.value()})
|
||||
# TODO: Recalculate placement from source geometry
|
||||
|
||||
elif ztools_type in ("normal_to_edge", "on_edge"):
|
||||
self._update_params({"parameter": self.param_spin.value()})
|
||||
# TODO: Recalculate placement from source geometry
|
||||
|
||||
# Update position display
|
||||
pos = self.datum_obj.Placement.Base
|
||||
self.pos_label.setText(f"({pos.x:.2f}, {pos.y:.2f}, {pos.z:.2f})")
|
||||
|
||||
App.ActiveDocument.recompute()
|
||||
|
||||
def _update_params(self, new_values):
|
||||
"""Update stored parameters with new values."""
|
||||
params_json = getattr(self.datum_obj, "ZTools_Params", "{}")
|
||||
try:
|
||||
params = json.loads(params_json)
|
||||
except json.JSONDecodeError:
|
||||
params = {}
|
||||
|
||||
params.update(new_values)
|
||||
|
||||
# Re-serialize (handle vectors)
|
||||
serializable = {}
|
||||
for k, v in params.items():
|
||||
if hasattr(v, "x") and hasattr(v, "y") and hasattr(v, "z"):
|
||||
serializable[k] = {"_type": "Vector", "x": v.x, "y": v.y, "z": v.z}
|
||||
else:
|
||||
serializable[k] = v
|
||||
|
||||
self.datum_obj.ZTools_Params = json.dumps(serializable)
|
||||
|
||||
def accept(self):
|
||||
"""Handle OK button - apply changes."""
|
||||
# Update label if changed
|
||||
new_label = self.name_edit.text().strip()
|
||||
if new_label and new_label != self.datum_obj.Label:
|
||||
self.datum_obj.Label = new_label
|
||||
|
||||
App.ActiveDocument.recompute()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
"""Handle Cancel button - restore original placement."""
|
||||
self.datum_obj.Placement = self.original_placement
|
||||
App.ActiveDocument.recompute()
|
||||
return True
|
||||
|
||||
def getStandardButtons(self):
|
||||
"""Return dialog buttons."""
|
||||
return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
|
||||
@@ -1,27 +1,29 @@
|
||||
# ztools/datums - Datum creation tools
|
||||
from .core import (
|
||||
# Planes
|
||||
plane_offset_from_face,
|
||||
plane_midplane,
|
||||
plane_from_3_points,
|
||||
plane_normal_to_edge,
|
||||
plane_angled,
|
||||
plane_tangent_to_cylinder,
|
||||
axis_cylinder_center,
|
||||
# Axes
|
||||
axis_from_2_points,
|
||||
axis_from_edge,
|
||||
axis_cylinder_center,
|
||||
axis_intersection_planes,
|
||||
plane_angled,
|
||||
plane_from_3_points,
|
||||
plane_midplane,
|
||||
plane_normal_to_edge,
|
||||
# Planes
|
||||
plane_offset_from_face,
|
||||
plane_offset_from_plane,
|
||||
plane_tangent_to_cylinder,
|
||||
point_at_coordinates,
|
||||
# Points
|
||||
point_at_vertex,
|
||||
point_at_coordinates,
|
||||
point_on_edge,
|
||||
point_center_of_face,
|
||||
point_center_of_circle,
|
||||
point_center_of_face,
|
||||
point_on_edge,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"plane_offset_from_face",
|
||||
"plane_offset_from_plane",
|
||||
"plane_midplane",
|
||||
"plane_from_3_points",
|
||||
"plane_normal_to_edge",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user