diff --git a/docs/INTEGRATION_PLAN.md b/docs/INTEGRATION_PLAN.md new file mode 100644 index 0000000000..d41d8e679e --- /dev/null +++ b/docs/INTEGRATION_PLAN.md @@ -0,0 +1,460 @@ +# Kindred Create Integration Plan + +This document outlines the strategy for integrating ztools and Silo workbenches as built-in addons in Kindred Create while maintaining clear boundaries with FreeCAD core. + +## Goals + +1. **Native feel** - ztools and Silo should feel like first-class citizens, not bolted-on addons +2. **Clean boundaries** - Clear separation between FreeCAD core, Kindred extensions, and addon code +3. **Minimal core modifications** - Preserve FreeCAD's container models (Part, Body, Assembly) +4. **Maintainability** - Easy to pull upstream FreeCAD changes without merge conflicts +5. **Extensibility** - Architecture that supports future Kindred-specific features + +## Current State + +### Repository Structure +``` +kindred-create/ +├── src/Mod/ # FreeCAD core modules (PartDesign, Sketcher, Assembly, etc.) +├── mods/ # Kindred addons (git submodules) +│ ├── ztools/ # Part design extensions, theme +│ └── silo/ # Parts database integration +└── resources/ # Branding, default preferences +``` + +### Integration Points Today +- **ztools**: Pure Python addon wrapping FreeCAD commands with enhanced UX +- **Silo**: Pure Python addon with REST API integration +- **Theme**: Preference pack + runtime stylesheet application + +## Architecture Layers + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Kindred Create Application │ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 4: Kindred Workbenches (mods/) │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ ztools │ │ Silo │ │ +│ │ - Datum Creator │ │ - Open/Save/Commit │ │ +│ │ - Enhanced Pocket │ │ - Part numbering │ │ +│ │ - Assembly Patterns│ │ - Revision control │ │ +│ │ - Spreadsheet fmt │ │ - BOM management │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 3: Kindred Core Extensions (src/Mod/Kindred/) │ +│ ┌─────────────────────────────────────────────────────────────┐│ +│ │ - KindredFeatures: Flip-side pocket, custom geometry ops ││ +│ │ - KindredGui: Shared UI components, selection helpers ││ +│ │ - Theme integration hooks ││ +│ └─────────────────────────────────────────────────────────────┘│ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 2: FreeCAD Python API │ +│ ┌─────────────────────────────────────────────────────────────┐│ +│ │ FreeCAD, FreeCADGui, Part, PartDesign, Sketcher, Assembly ││ +│ └─────────────────────────────────────────────────────────────┘│ +├─────────────────────────────────────────────────────────────────┤ +│ Layer 1: FreeCAD Core (C++) │ +│ ┌─────────────────────────────────────────────────────────────┐│ +│ │ App::Document, Part::Feature, PartDesign::Body, etc. ││ +│ └─────────────────────────────────────────────────────────────┘│ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Boundary Definitions + +### Layer 1: FreeCAD Core (DO NOT MODIFY) +**Location**: `src/Mod/PartDesign/App/`, `src/Mod/Part/App/`, etc. + +These are FreeCAD's fundamental data structures and should remain untouched: +- `PartDesign::Body` - Feature container +- `PartDesign::Pocket`, `PartDesign::Pad` - Additive/subtractive features +- `Part::Feature` - Base geometric feature +- `App::Document` - Document container +- `Sketcher::SketchObject` - 2D constraint system + +**Rationale**: Modifying these creates merge conflicts with upstream FreeCAD and risks breaking compatibility with existing FCStd files. + +### Layer 2: FreeCAD Python API (USE AS-IS) +**Access via**: `import FreeCAD`, `import FreeCADGui`, `import Part`, etc. + +The Python API provides everything needed for feature creation: +- Create objects: `body.newObject("PartDesign::Pocket", "Pocket")` +- Set properties: `pocket.Length = 10.0` +- Register commands: `FreeCADGui.addCommand("Name", CommandClass())` +- Access geometry: `shape.Faces`, `shape.Edges`, `shape.Vertexes` + +### Layer 3: Kindred Core Extensions (NEW - MINIMAL) +**Location**: `src/Mod/Kindred/` (to be created) + +A thin C++ module providing capabilities that cannot be achieved in pure Python: + +| Component | Purpose | Justification | +|-----------|---------|---------------| +| `CreateFeatures` | Custom PartDesign-like features | Python feature objects have performance limitations for complex boolean operations | +| `CreateGui` | Shared UI utilities | Common selection helpers, task panel base classes | +| `ThemeHooks` | Theme application entry points | Ensure theme applies before any workbench loads | + +**Namespace**: All Kindred Create features use the `Create::` prefix (e.g., `Create::FlipPocket`). + +**Design principle**: Only add C++ code when Python cannot achieve the requirement. Document why each component exists. + +### Layer 4: Kindred Workbenches (ADDON PATTERN) +**Location**: `mods/ztools/`, `mods/silo/` + +Pure Python workbenches following FreeCAD's addon pattern: +- Self-contained with `InitGui.py`, `Init.py`, `package.xml` +- Register commands via `FreeCADGui.addCommand()` +- Define toolbars/menus via `Workbench.appendToolbar()` +- Can be developed/tested independently + +## Detailed Integration Plan + +### Phase 1: Addon Auto-Loading + +**Goal**: ztools and Silo load automatically without user intervention. + +**Implementation**: + +1. **Create addon manifest** (`src/Mod/Kindred/addons.json`): +```json +{ + "builtin_addons": [ + { + "name": "ztools", + "path": "mods/ztools/ztools", + "autoload": true, + "workbench": "ZToolsWorkbench" + }, + { + "name": "silo", + "path": "mods/silo/pkg/freecad", + "autoload": true, + "workbench": "SiloWorkbench" + } + ] +} +``` + +2. **Modify addon path discovery** (`src/Mod/Kindred/Init.py`): +```python +# Add mods/ directory to FreeCAD's module search path +import FreeCAD +import os + +mods_dir = os.path.join(FreeCAD.getHomePath(), "mods") +if os.path.isdir(mods_dir): + for addon in os.listdir(mods_dir): + addon_path = os.path.join(mods_dir, addon) + if os.path.isdir(addon_path) and addon_path not in sys.path: + sys.path.insert(0, addon_path) +``` + +3. **Set default workbench** in preferences: +```xml + +ZToolsWorkbench +``` + +**Files to create/modify**: +- Create: `src/Mod/Kindred/Init.py` +- Create: `src/Mod/Kindred/InitGui.py` +- Create: `src/Mod/Kindred/CMakeLists.txt` +- Modify: `src/Mod/CMakeLists.txt` (add Kindred subdirectory) +- Modify: `resources/preferences/KindredCreate/KindredCreate.cfg` + +### Phase 2: Enhanced Pocket as Separate Feature + +**Goal**: "Flip Side to Cut" becomes a proper feature, not a command wrapper. + +**Current state** (in ztools): +```python +# ztools/commands/pocket_commands.py +class ZTools_EnhancedPocket: + def Activated(self): + # Creates standard Pocket, then applies boolean Common + # Workaround using existing features +``` + +**Proposed architecture**: + +``` +src/Mod/Create/ +├── App/ +│ ├── CreateFeatures.cpp # Feature implementations +│ ├── FeatureFlipPocket.cpp # Flip-side pocket feature +│ └── FeatureFlipPocket.h +├── Gui/ +│ ├── Command.cpp # Command registrations +│ ├── TaskFlipPocket.cpp # Task panel +│ └── ViewProviderFlipPocket.cpp +└── CMakeLists.txt +``` + +**Feature design** (`Create::FlipPocket`): + +```cpp +// Inherits from PartDesign::ProfileBased (same base as Pocket) +class FeatureFlipPocket : public PartDesign::ProfileBased { +public: + // Properties (same as Pocket, plus flip flag) + App::PropertyLength Length; + App::PropertyEnumeration Type; // Dimension, ThroughAll, ToFirst, UpToFace + App::PropertyBool Symmetric; + App::PropertyBool FlipSide; // NEW: Cut outside instead of inside + + // Implementation uses Boolean Common instead of Cut when FlipSide=true + App::DocumentObjectExecReturn* execute(); +}; +``` + +**Separation of concerns**: +- **FreeCAD Core** (`PartDesign::Pocket`): Standard inside-cut behavior, unchanged +- **Create Extension** (`Create::FlipPocket`): Outside-cut using boolean common +- **ztools Workbench**: Provides UI command that creates `Create::FlipPocket` + +**Files to create**: +- `src/Mod/Create/App/FeatureFlipPocket.cpp` +- `src/Mod/Create/App/FeatureFlipPocket.h` +- `src/Mod/Create/Gui/TaskFlipPocket.cpp` +- `src/Mod/Create/Gui/ViewProviderFlipPocket.cpp` + +**ztools update**: +```python +# mods/ztools/ztools/commands/pocket_commands.py +class ZTools_EnhancedPocket: + def Activated(self): + # Now creates Create::FlipPocket instead of workaround + body = FreeCADGui.ActiveDocument.ActiveView.getActiveObject("pdbody") + pocket = body.newObject("Create::FlipPocket", "FlipPocket") + pocket.Profile = sketch + # Show task panel... +``` + +### Phase 3: Datum System Integration + +**Goal**: ztools datum creation uses stable, efficient C++ geometry calculations. + +**Current state**: Pure Python geometry calculations in `ztools/datums/core.py`. + +**Issue**: Python geometry operations can be slow and less precise for complex cases. + +**Proposed solution**: Create C++ helper functions, expose via Python. + +```cpp +// src/Mod/Create/App/DatumHelpers.cpp +namespace Create { + // Calculate midplane between two parallel faces + gp_Pln computeMidplane(const TopoDS_Face& face1, const TopoDS_Face& face2); + + // Calculate plane through three points + gp_Pln computePlaneFrom3Points(gp_Pnt p1, gp_Pnt p2, gp_Pnt p3); + + // Calculate axis at cylinder center + gp_Ax1 computeCylinderAxis(const TopoDS_Face& cylinderFace); + + // ... other datum calculations ... +} +``` + +**Python binding**: +```python +# In ztools after Create module is available +from Create import DatumHelpers + +plane = DatumHelpers.computeMidplane(face1, face2) +``` + +**Separation of concerns**: +- **FreeCAD Core** (`PartDesign::DatumPlane`): Data structure, unchanged +- **Create Extension** (`DatumHelpers`): Geometry calculation utilities +- **ztools Workbench**: UI, selection handling, property storage + +### Phase 4: Theme System Refinement + +**Goal**: Theme applies consistently at startup, no workbench dependency. + +**Current state**: Theme applied when ztools workbench activates. + +**Issue**: If user opens FreeCAD and doesn't activate ztools, theme isn't applied. + +**Proposed solution**: + +1. **Move theme to Create module** (`src/Mod/Create/InitGui.py`): +```python +# This runs at GUI startup, before any workbench +def applyKindredTheme(): + from PySide import QtWidgets + qss_path = os.path.join(FreeCAD.getResourceDir(), + "preferences", "KindredCreate", "KindredCreate.qss") + with open(qss_path) as f: + QtWidgets.QApplication.instance().setStyleSheet(f.read()) + + # Apply spreadsheet colors + applySpreadsheetColors() + +# Run at import time +applyKindredTheme() +``` + +2. **Remove theme code from ztools**: ztools focuses on commands, not theming. + +3. **Ensure load order**: Create module loads before other workbenches via `src/Mod/CMakeLists.txt` ordering. + +### Phase 5: Silo Deep Integration + +**Goal**: Silo commands available globally, not just in Silo workbench. + +**Current state**: Must switch to Silo workbench to access commands. + +**Proposed solution**: + +1. **Global menu registration** (`src/Mod/Create/InitGui.py`): +```python +def setupSiloMenu(): + # Add Silo menu to menu bar regardless of active workbench + import silo_commands + + mw = FreeCADGui.getMainWindow() + menuBar = mw.menuBar() + + siloMenu = QtWidgets.QMenu("Silo", mw) + menuBar.addMenu(siloMenu) + + for cmd in ["Silo_Open", "Silo_Save", "Silo_Commit", "Silo_Pull", "Silo_Push"]: + action = FreeCADGui.Command.get(cmd).getAction() + siloMenu.addAction(action[0]) +``` + +2. **Keyboard shortcuts** (global): +```python +# Ctrl+Shift+O: Silo Open +# Ctrl+Shift+S: Silo Save +# Ctrl+Shift+C: Silo Commit +``` + +3. **Status bar integration**: Show current Silo item info in status bar. + +### Phase 6: Build System Integration + +**Goal**: mods/ submodules installed correctly during build. + +**Implementation** (`src/Mod/Create/CMakeLists.txt`): + +```cmake +# Install ztools workbench +install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/ztools/ztools + DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/ztools) +install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/ztools/CatppuccinMocha + DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/ztools) +install(FILES ${CMAKE_SOURCE_DIR}/mods/ztools/package.xml + DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/ztools) + +# Install Silo workbench +install(DIRECTORY ${CMAKE_SOURCE_DIR}/mods/silo/pkg/freecad/ + DESTINATION ${CMAKE_INSTALL_DATADIR}/Mod/Silo) +``` + +## File Organization Summary + +### New files to create + +``` +src/Mod/Create/ # NEW: Kindred Create core extensions +├── CMakeLists.txt +├── Init.py # Addon path setup +├── InitGui.py # Theme application, global menus +├── App/ +│ ├── CMakeLists.txt +│ ├── CreateModule.cpp # Module registration +│ ├── FeatureFlipPocket.cpp/h # Flip-side pocket feature +│ └── DatumHelpers.cpp/h # Datum geometry utilities +└── Gui/ + ├── CMakeLists.txt + ├── CreateGuiModule.cpp + ├── Command.cpp # Create-specific commands + ├── TaskFlipPocket.cpp/h + └── ViewProviderFlipPocket.cpp/h +``` + +### Files to modify + +| File | Change | +|------|--------| +| `src/Mod/CMakeLists.txt` | Add `add_subdirectory(Create)` | +| `resources/preferences/KindredCreate/KindredCreate.cfg` | Set default workbench | +| `mods/ztools/ztools/commands/pocket_commands.py` | Use `Create::FlipPocket` | +| `mods/ztools/ztools/datums/core.py` | Use `Create.DatumHelpers` when available | + +### Files to remove/deprecate from ztools + +| File | Reason | +|------|--------| +| `ztools/resources/theme.py` | Moved to Create module | +| Theme application in `InitGui.py` | Handled globally | + +## Implementation Priority + +| Priority | Phase | Effort | Impact | +|----------|-------|--------|--------| +| 1 | Phase 1: Addon Auto-Loading | Low | High - Seamless user experience | +| 2 | Phase 4: Theme System | Low | High - Consistent appearance | +| 3 | Phase 5: Silo Global Menu | Medium | High - Always-available database access | +| 4 | Phase 2: Enhanced Pocket | High | Medium - Proper feature architecture | +| 5 | Phase 3: Datum Helpers | Medium | Medium - Performance improvement | +| 6 | Phase 6: Build System | Low | High - Clean distribution | + +## Testing Strategy + +### Unit Tests +- Create feature creation and execution +- Datum helper calculations +- Theme application verification + +### Integration Tests +- Addon auto-loading on fresh install +- Feature creation via ztools commands +- Silo operations with mock server +- Theme persistence across sessions + +### Compatibility Tests +- Open existing FCStd files (no regressions) +- Export to STEP/IGES (geometry unchanged) +- Upstream FreeCAD file compatibility + +## Migration Notes + +### For existing ztools/silo users +- No changes required - workbenches continue to function +- Enhanced features available automatically when Create module present +- Theme applies globally instead of per-workbench + +### For developers +- ztools can check for Create module availability: +```python +try: + import Create + HAS_CREATE = True +except ImportError: + HAS_CREATE = False + +# Use C++ implementation if available, fall back to Python +if HAS_CREATE: + plane = Create.DatumHelpers.computeMidplane(f1, f2) +else: + plane = compute_midplane_python(f1, f2) +``` + +## Design Decisions + +1. **Naming convention**: Kindred Create features use the `Create::` prefix (e.g., `Create::FlipPocket`, `Create::DatumHelpers`) to clearly identify them as Kindred Create extensions separate from FreeCAD core. + +2. **Upstream contribution**: Kindred Create is a standalone product and does not plan to contribute features upstream to FreeCAD. This allows for divergent design decisions optimized for Kindred Create's target use cases. + +3. **Silo server distribution**: Silo server is distributed separately from Kindred Create. Users download and deploy the Silo server independently. Setup instructions are documented in `mods/silo/README.md`. + +4. **Version synchronization**: ztools and Silo versions are determined by pinned git submodule commits. Updates are deliberate and tested before each Kindred Create release. To update: + ```bash + cd mods/ztools && git pull origin main && cd ../.. + git add mods/ztools && git commit -m "Update ztools to " + ```