- Define 4-layer architecture (FreeCAD Core, Python API, Create Extensions, Workbenches) - Plan src/Mod/Create/ module for C++ features (Create::FlipPocket, DatumHelpers) - Document 6 implementation phases with priorities - Use Create:: namespace prefix for all Kindred Create features - Silo server distributed separately - ztools/Silo versions pinned via git submodules
18 KiB
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
- Native feel - ztools and Silo should feel like first-class citizens, not bolted-on addons
- Clean boundaries - Clear separation between FreeCAD core, Kindred extensions, and addon code
- Minimal core modifications - Preserve FreeCAD's container models (Part, Body, Assembly)
- Maintainability - Easy to pull upstream FreeCAD changes without merge conflicts
- 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 containerPartDesign::Pocket,PartDesign::Pad- Additive/subtractive featuresPart::Feature- Base geometric featureApp::Document- Document containerSketcher::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:
- Create addon manifest (
src/Mod/Kindred/addons.json):
{
"builtin_addons": [
{
"name": "ztools",
"path": "mods/ztools/ztools",
"autoload": true,
"workbench": "ZToolsWorkbench"
},
{
"name": "silo",
"path": "mods/silo/pkg/freecad",
"autoload": true,
"workbench": "SiloWorkbench"
}
]
}
- Modify addon path discovery (
src/Mod/Kindred/Init.py):
# 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)
- Set default workbench in preferences:
<!-- resources/preferences/KindredCreate/KindredCreate.cfg -->
<FCText Name="StartUpModule">ZToolsWorkbench</FCText>
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):
# 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):
// 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.cppsrc/Mod/Create/App/FeatureFlipPocket.hsrc/Mod/Create/Gui/TaskFlipPocket.cppsrc/Mod/Create/Gui/ViewProviderFlipPocket.cpp
ztools update:
# 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.
// 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:
# 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:
- Move theme to Create module (
src/Mod/Create/InitGui.py):
# 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()
-
Remove theme code from ztools: ztools focuses on commands, not theming.
-
Ensure load order: Create module loads before other workbenches via
src/Mod/CMakeLists.txtordering.
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:
- Global menu registration (
src/Mod/Create/InitGui.py):
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])
- Keyboard shortcuts (global):
# Ctrl+Shift+O: Silo Open
# Ctrl+Shift+S: Silo Save
# Ctrl+Shift+C: Silo Commit
- 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):
# 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:
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
-
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. -
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.
-
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. -
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:
cd mods/ztools && git pull origin main && cd ../.. git add mods/ztools && git commit -m "Update ztools to <version>"