# 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 " ```