Commit Graph

42814 Commits

Author SHA1 Message Date
forbes
1b01762a1c chore: rebrand XDGData and icon installs to Kindred Create (#367)
All checks were successful
Build and Test / build (pull_request) Successful in 29m26s
- Replace org.freecad.FreeCAD.desktop with kindred-create.desktop
- Replace org.freecad.FreeCAD.metainfo.xml.in with Kindred Create
  metainfo (new description, Kindred URLs, kindred-create app ID)
- Replace org.freecad.FreeCAD.xml with kindred-create.xml (adds .kc
  MIME type alongside .fcstd)
- Replace FreeCAD.thumbnailer.in with kindred-create.thumbnailer.in
- Update XDGData/CMakeLists.txt for new filenames
- Update src/Gui/CMakeLists.txt icon installs: use kindred-create
  icons from resources/ (all sizes 16-512 + SVG) instead of renaming
  freecad-icon-*.png to org.freecad.FreeCAD.png
- Update freecad-thumbnailer.in default icon to kindred-create.png
- Update AppImage create_bundle.sh to use kindred-create desktop/icon

Remaining org.freecad.FreeCAD references are macOS bundle IDs
(MainWindow.cpp, MacAppBundle/) and packaging fallbacks — separate
rebranding tasks.
2026-03-02 08:06:36 -06:00
forbes
485f69f257 chore: remove ZTools addon from build and loader (#344)
All checks were successful
Build and Test / build (pull_request) Successful in 29m28s
- Remove ZTools install block from src/Mod/Create/CMakeLists.txt
- Remove mods/ztools submodule entry from .gitmodules
- Remove 'ztools' from legacy fallback order in addon_loader.py
- Remove ztools imports and test classes from test_kindred_pure.py
  (TestTypeMatches, TestMatchScore, TestSelectionItemProperties,
   TestColumnToIndex, TestDatumModes)
- Remove 'ztools Workbench' from issue template component lists
- Remove mods/ztools submodule from git tracking

ZTools will be archived to a reference folder in a separate step (#345).
This is part of the UI/UX rework epic (#346).
2026-02-27 12:46:32 -06:00
forbes
d47c94af8d chore: remove QuickNav addon from build and loader (#343)
All checks were successful
Build and Test / build (pull_request) Successful in 30m57s
- Remove QuickNav install block from src/Mod/Create/CMakeLists.txt
- Remove mods/quicknav submodule entry from .gitmodules
- Remove quicknav imports and TestWorkbenchMap tests from test_kindred_pure.py
- Remove mods/quicknav submodule from git tracking

QuickNav will be archived to a reference folder in a separate step (#345).
This is part of the UI/UX rework epic (#346).
2026-02-27 12:27:33 -06:00
forbes
d174ef7a8d fix(assembly): use short-arc angle in validateNewPlacements (#338)
All checks were successful
Build and Test / build (pull_request) Successful in 30m5s
Rotation::evaluateVector() computes angle = 2*acos(w) which gives
values in [0, 2*pi].  When the relative quaternion has w < 0 (opposite
hemisphere), the angle exceeds pi even though q and -q represent the
same rotation.  This caused the validator to report ~350 degree 'flips'
and reject valid solver output.

Fix: map the angle to [0, pi] before comparing against the 91-degree
threshold.  This is the short-arc equivalent — the minimum rotation
angle between two orientations regardless of quaternion sign convention.
2026-02-27 10:38:37 -06:00
69ccdbf742 Merge pull request 'fix(assembly): use instance suffixes for duplicate part labels' (#335) from fix/assembly-part-number-suffix into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #335
2026-02-26 16:34:34 +00:00
7c85b2ad93 fix(assembly): use instance suffixes for duplicate part labels
All checks were successful
Build and Test / build (pull_request) Successful in 29m11s
When parts with structured part numbers (e.g., P03-0001) are inserted
into an assembly multiple times, UniqueNameManager::decomposeName()
treats the trailing digits as an auto-generated suffix and increments
them (P03-0002, P03-0003), corrupting the part number.

Add a makeInstanceLabel() helper in AssemblyLink.cpp that appends -N
instance suffixes instead (P03-0001-1, P03-0001-2). All instances get
a suffix starting at -1 so the original part number is never modified.

Applied at all three Label.setValue() sites in
synchronizeComponents() (AssemblyLink, link group, and regular link
creation paths).

Also add a UniqueNameManager test documenting the trailing-digit
decomposition behavior for structured part numbers.

Closes #327
2026-02-26 09:00:13 -06:00
311d911cfa fix(theme): prevent panel element headings from being clipped
All checks were successful
Build and Test / build (pull_request) Successful in 29m23s
Three QSS issues caused headings to render with only the top ~60%
visible:

- QGroupBox: margin-top 12px was insufficient for the title rendered
  in subcontrol-origin: margin. Increased to 16px and added 2px
  vertical padding to the title.
- QDockWidget::title: min-height 18px conflicted with padding 8px 6px,
  constraining the content area. Removed min-height to let Qt auto-size
  from padding + font metrics.
- QSint--ActionGroup QToolButton: min-height 18px forced a height that
  was then clipped by the C++ setFixedHeight(headerSize) calculation.
  Set min-height to 0px so the C++ layout controls sizing.

Closes #325
2026-02-26 08:48:01 -06:00
e73c5fc750 feat: add QuickNav addon — Phase 1 core infrastructure (#320)
All checks were successful
Build and Test / build (pull_request) Successful in 29m58s
Add quicknav submodule and create-side integration for keyboard-driven
command navigation.

Submodule: mods/quicknav (https://git.kindred-systems.com/kindred/quicknav)

Create-side changes:
- CMakeLists.txt: add quicknav install rules
- test_kindred_pure.py: add 16 workbench_map validation tests
- docs/src/quicknav/SPEC.md: QuickNav specification

QuickNav provides numbered-key access to workbenches (Ctrl+1-5),
command groupings (Shift+1-9), and individual commands (1-9), with
a navigation bar toolbar and input-widget safety guards.
2026-02-23 14:12:02 -06:00
forbes
14ee8c673f fix(assembly): classify datum planes from all class hierarchies in Distance joints
Some checks failed
Build and Test / build (pull_request) Has been cancelled
The datum plane detection in getDistanceType() only checked for
App::Plane (origin planes). This missed two other class hierarchies:

  - PartDesign::Plane (inherits Part::Datum, NOT App::Plane)
  - Part::Plane primitive referenced bare (no Face element)

Both produce empty element types (sub-name ends with ".") but failed
the isDerivedFrom<App::Plane>() check, falling through to
DistanceType::Other and the Planar fallback. This caused incorrect
constraint geometry, leading to conflicting/unsatisfiable constraints
and solver failures.

Add shape-based isDatumPlane/Line/Point helpers that cover all three
hierarchies by inspecting the actual OCCT geometry rather than relying
on class identity alone. Extend getDistanceType() to use these helpers
for all datum-vs-datum and datum-vs-element combinations.

Adds TestDatumClassification.py with tests for PartDesign::Plane,
Part::Plane (bare ref), and cross-hierarchy datum combinations.
2026-02-22 21:18:34 -06:00
forbes
962b521f5c fix(assembly): classify datum planes from all class hierarchies in Distance joints
All checks were successful
Build and Test / build (pull_request) Successful in 30m13s
The datum plane detection in getDistanceType() only checked for
App::Plane (origin planes). This missed two other class hierarchies:

  - PartDesign::Plane (inherits Part::Datum, NOT App::Plane)
  - Part::Plane primitive referenced bare (no Face element)

Both produce empty element types (sub-name ends with ".") but failed
the isDerivedFrom<App::Plane>() check, falling through to
DistanceType::Other and the Planar fallback. This caused incorrect
constraint geometry, leading to conflicting/unsatisfiable constraints
and solver failures.

Add shape-based isDatumPlane/Line/Point helpers that cover all three
hierarchies by inspecting the actual OCCT geometry rather than relying
on class identity alone. Extend getDistanceType() to use these helpers
for all datum-vs-datum and datum-vs-element combinations.

Adds TestDatumClassification.py with tests for PartDesign::Plane,
Part::Plane (bare ref), and cross-hierarchy datum combinations.
2026-02-22 18:55:39 -06:00
forbes
b4835e1b05 fix(assembly): classify datum plane references in Distance joints
Some checks failed
Build and Test / build (pull_request) Has been cancelled
When a Distance joint references a datum plane (XY_Plane, XZ_Plane,
YZ_Plane), getDistanceType() failed to recognize it because datum
plane sub-names yield an empty element type. This caused the fallback
to DistanceType::Other → BaseJointKind::Planar, which adds spurious
parallel-normal residuals that overconstrain the system.

For example, three vertex-to-datum-plane Distance joints produced
10 residuals (3×Planar) with 6 mutually contradictory orientation
constraints, causing the solver to find garbage least-squares
solutions.

Add early detection of App::Plane datum objects before the main
geometry classification chain. Datum planes are now correctly mapped:
- Vertex + DatumPlane → PointPlane → PointInPlane (1 residual)
- Edge + DatumPlane → LinePlane → LineInPlane
- Face/DatumPlane + DatumPlane → PlanePlane → Planar
2026-02-22 14:19:11 -06:00
forbes
e5b07449d7 fix(assembly): classify datum plane references in Distance joints
Some checks failed
Build and Test / build (pull_request) Has been cancelled
When a Distance joint references a datum plane (XY_Plane, XZ_Plane,
YZ_Plane), getDistanceType() failed to recognize it because datum
plane sub-names yield an empty element type. This caused the fallback
to DistanceType::Other → BaseJointKind::Planar, which adds spurious
parallel-normal residuals that overconstrain the system.

For example, three vertex-to-datum-plane Distance joints produced
10 residuals (3×Planar) with 6 mutually contradictory orientation
constraints, causing the solver to find garbage least-squares
solutions.

Add early detection of App::Plane datum objects before the main
geometry classification chain. Datum planes are now correctly mapped:
- Vertex + DatumPlane → PointPlane → PointInPlane (1 residual)
- Edge + DatumPlane → LinePlane → LineInPlane
- Face/DatumPlane + DatumPlane → PlanePlane → Planar
2026-02-22 12:24:44 -06:00
forbes
a10b9d9a9f fix(assembly): classify datum plane references in Distance joints
All checks were successful
Build and Test / build (pull_request) Successful in 41m13s
When a Distance joint references a datum plane (XY_Plane, XZ_Plane,
YZ_Plane), getDistanceType() failed to recognize it because datum
plane sub-names yield an empty element type. This caused the fallback
to DistanceType::Other → BaseJointKind::Planar, which adds spurious
parallel-normal residuals that overconstrain the system.

For example, three vertex-to-datum-plane Distance joints produced
10 residuals (3×Planar) with 6 mutually contradictory orientation
constraints, causing the solver to find garbage least-squares
solutions.

Add early detection of App::Plane datum objects before the main
geometry classification chain. Datum planes are now correctly mapped:
- Vertex + DatumPlane → PointPlane → PointInPlane (1 residual)
- Edge + DatumPlane → LinePlane → LineInPlane
- Face/DatumPlane + DatumPlane → PlanePlane → Planar
2026-02-21 22:04:18 -06:00
441cf9e826 Merge pull request 'feat(assembly): add diagnostic logging to solver and assembly' (#313) from feat/solver-diagnostic-logging into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #313
2026-02-21 16:11:34 +00:00
forbes
c682c5d153 feat(assembly): add diagnostic logging to solver and assembly
Some checks failed
Build and Test / build (pull_request) Has been cancelled
C++ (AssemblyObject):
- getOrCreateSolver: log which solver backend was loaded
- solve: log assembly name, grounded/joint counts, context size,
  result status with DOF and placement count, per-constraint
  diagnostics on failure
- preDrag/doDragStep/postDrag: log drag part count, per-step
  validation failures, and summary (total steps / rejected count)
- buildSolveContext: log grounded/free part counts, constraint count,
  limits count, and bundle_fixed flag

Python (kindred_solver submodule):
- solver.py: log solve entry/exit with timing, system build stats,
  decomposition decisions, Newton/BFGS fallback events, drag lifecycle
- decompose.py: log cluster stats and per-cluster convergence
- Init.py: FreeCAD log handler routing Python logging to Console
2026-02-21 10:08:51 -06:00
f65a4a5e2b Merge pull request 'fix(assembly): update flip-detection baseline during drag steps' (#312) from fix/assembly-drag-flip-detection into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #312
2026-02-21 15:59:55 +00:00
forbes
5d55f091d0 fix(assembly): update flip-detection baseline during drag steps
Some checks failed
Build and Test / build (pull_request) Has been cancelled
During drag operations, validateNewPlacements() compared each solver
result against the pre-drag positions saved once in preDrag().  As the
user dragged further, the cumulative rotation from that fixed baseline
easily exceeded the 91-degree threshold, causing valid intermediate
results to be rejected with 'flipped orientation' warnings and making
parts appear to explode.

Fix: call savePlacementsForUndo() after each accepted drag step so
that the flip check compares against the last accepted state rather
than the original pre-drag origin.
2026-02-21 09:59:04 -06:00
a445275fd2 Merge pull request 'fix(kc_format): eliminate duplicate silo/manifest.json in .kc files' (#311) from fix/kc-duplicate-manifest into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #311
2026-02-21 15:50:58 +00:00
forbes
88efa2a6ae fix(kc_format): eliminate duplicate silo/manifest.json entries in .kc files
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Two code paths were appending silo/manifest.json to the ZIP without
removing the previous entry, causing Python's zipfile module to warn
about duplicate names:

1. slotFinishSaveDocument() re-injected the cached manifest from
   entries, then the modified_at update branch wrote a second copy.

2. update_manifest_fields() opened the ZIP in append mode and wrote
   an updated manifest without removing the old one.

Fix slotFinishSaveDocument() by preparing the final manifest (with
updated modified_at) in the entries dict before writing, so only one
copy is written to the ZIP.

Fix update_manifest_fields() by rewriting the ZIP atomically via a
temp file, deduplicating any pre-existing duplicate entries in the
process.
2026-02-21 09:49:36 -06:00
62f077a267 Merge pull request 'fix(assembly): prevent segfault when all joints are removed' (#310) from fix/assembly-empty-joints-segfault into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #310
2026-02-21 15:47:59 +00:00
forbes
b6b0ebb4dc fix(assembly): prevent segfault when all joints are removed
Some checks failed
Build and Test / build (pull_request) Has been cancelled
updateSolveStatus() calls solve() when lastResult_.placements is empty,
but solve() calls updateSolveStatus() at the end. When an assembly has
zero constraints (all joints removed), the solver returns zero
placements, causing infinite recursion until stack overflow (segfault).

Add a static re-entrancy guard so the recursive solve() call is skipped
if updateSolveStatus() is already on the call stack.
2026-02-21 09:47:15 -06:00
forbes
5883ac8a0d fix(gui): resolve PartDesign toolbars and breadcrumb when editing body inside assembly
Some checks failed
Build and Test / build (pull_request) Has been cancelled
When double-clicking a PartDesign Body inside an Assembly, the editing
context resolver failed to show PartDesign toolbars or a proper
breadcrumb. Three root causes:

1. Priority preemption: assembly.edit (priority 90) always matched
   because the Assembly VP remained in edit mode, blocking all
   PartDesign contexts (priorities 30-40).

2. Wrong active-object key: PartDesign matchers only queried the
   "part" active object, but ViewProviderBody::toggleActiveBody()
   sets "part" to the containing App::Part (which is the Assembly
   itself). The Body is set under "pdbody", which was never queried.

3. Missing refresh trigger: ActiveObjectList::setObject() fires
   Document::signalActivatedViewProvider, but EditingContextResolver
   had no connection to it, so setActiveObject("pdbody", body) never
   triggered a context re-resolve.

Changes:
- Forward signalActivatedViewProvider from Gui::Document to
  Gui::Application (same pattern as signalInEdit/signalResetEdit)
- Connect EditingContextResolver to the new application-level signal
- Add getActivePdBodyObject() helper querying the "pdbody" key
- Add partdesign.in_assembly context (priority 95) that matches when
  a Body is active pdbody while an Assembly is in edit
- Update partdesign.body and partdesign.feature matchers to check
  pdbody before falling back to the part key
- Add Assembly > Body breadcrumb with Blue > Mauve coloring
- Update label resolution to prefer pdbody name
2026-02-21 09:43:51 -06:00
0f8fa0be86 Merge pull request 'feat(assembly): fixed reference planes (Top/Front/Right) + solver docs' (#307) from feat/assembly-origin-planes into main
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 51s
Build and Test / build (push) Has been cancelled
Reviewed-on: #307
2026-02-21 15:09:55 +00:00
forbes
acc255972d feat(assembly): fixed reference planes + solver docs
Some checks failed
Build and Test / build (pull_request) Failing after 7m52s
Assembly Origin Planes:
- AssemblyObject::setupObject() relabels origin planes to
  Top (XY), Front (XZ), Right (YZ) on assembly creation
- CommandCreateAssembly.py makes origin planes visible by default
- AssemblyUtils.cpp getObjFromRef() resolves LocalCoordinateSystem
  to child datum elements for joint references to origin planes
- TestAssemblyOriginPlanes.py: 9 integration tests covering
  structure, labels, grounding, reference resolution, solver,
  and save/load round-trip

Solver Documentation:
- docs/src/solver/: 7 new pages covering architecture overview,
  expression DAG, constraints, solving algorithms, diagnostics,
  assembly integration, and writing custom solvers
- docs/src/SUMMARY.md: added Kindred Solver section
2026-02-21 09:09:16 -06:00
forbes
6e7d2b582e fix(assembly): move resetSolver() out-of-line to fix incomplete type error
Some checks failed
Build and Test / build (pull_request) Has been cancelled
unique_ptr::reset() requires the complete type for its deleter, but
IKCSolver is only forward-declared in AssemblyObject.h. Move the
definition to AssemblyObject.cpp where the full header is included.
2026-02-21 07:08:59 -06:00
forbes
72e7e32133 feat(solver): KCSolve solver addon with assembly integration (#289)
Some checks failed
Build and Test / build (pull_request) Has been cancelled
Adds the Kindred constraint solver as a pluggable Assembly workbench
backend, covering phases 3d through 5 of the solver roadmap.

Phase 3d: SolveContext packing
- Pack/unpack SolveContext into .kc archive on document save

Solver addon (mods/solver):
- Phase 1: Expression DAG, Newton-Raphson + BFGS, 3 basic constraints
- Phase 2: Full constraint vocabulary — all 24 BaseJointKind types
- Phase 3: Graph decomposition for cluster-by-cluster solving
- Phase 4: Per-entity DOF diagnostics, overconstrained detection,
  half-space preference tracking, minimum-movement weighting
- Phase 5: _build_system extraction, diagnose(), drag protocol,
  joint limits warning

Assembly workbench integration:
- Preference-driven solver selection (reads Mod/Assembly/Solver param)
- Solver backend combo box in Assembly preferences UI
- resetSolver() on AssemblyObject for live preference switching
- Integration tests (TestKindredSolverIntegration.py)
- In-client console test script (console_test_phase5.py)
2026-02-20 23:47:50 -06:00
311b3ea4f1 Merge pull request 'fix(gui): complete toolbar whitelists in EditingContextResolver' (#301) from fix/toolbar-context-whitelists into main
All checks were successful
Build and Test / build (push) Successful in 30m19s
Reviewed-on: #301
2026-02-20 18:14:11 +00:00
forbes
686d8699c9 fix(gui): complete toolbar whitelists in EditingContextResolver
All checks were successful
Build and Test / build (pull_request) Successful in 29m59s
The EditingContextResolver controls toolbar visibility via explicit
whitelists per editing context. Several contexts had incomplete lists,
causing workbench toolbars to be missing compared to base FreeCAD.

Changes:

partdesign.feature (priority 40):
  - Add 'Sketcher' toolbar so users can create new sketches from an
    active Body with features

partdesign.body (priority 30):
  - Add Modeling, Dress-Up, and Transformation toolbars (previously
    only showed Helper + Sketcher)

partdesign.workbench (priority 20):
  - Add Modeling, Dress-Up, and Transformation toolbars (same as body)

sketcher.workbench (priority 20):
  - Add Geometries, Constraints, B-Spline Tools, Visual Helpers
    (previously only showed Sketcher + Sketcher Tools)

assembly.idle (priority 30):
  - Add 'Assembly Joints' and 'Assembly Management' toolbars

assembly.workbench (priority 20):
  - Add 'Assembly Joints' and 'Assembly Management' toolbars

No changes to sketcher.edit or assembly.edit contexts — those were
already correct.
2026-02-20 12:13:23 -06:00
forbes
7e766a228e feat(kcsolve): add to_dict()/from_dict() JSON serialization for all types
All checks were successful
Build and Test / build (pull_request) Successful in 31m19s
Phase 3a of the solver server integration: add dict/JSON serialization
to all KCSolve pybind11 types so SolveContext and SolveResult can be
transported as JSON between the Create client, Silo server, and solver
runners.

Implementation:
- Constexpr enum string mapping tables for all 5 enums (BaseJointKind,
  SolveStatus, DiagnosticKind, MotionKind, LimitKind) with template
  bidirectional lookup helpers
- File-local to_dict/from_dict conversion functions for all 10 types
  (Transform, Part, Constraint::Limit, Constraint, MotionDef,
  SimulationParams, SolveContext, ConstraintDiagnostic,
  SolveResult::PartResult, SolveResult)
- .def("to_dict") and .def_static("from_dict") on every py::class_<>
  binding chain

Serialization details per SOLVER.md §3:
- SolveContext.to_dict() includes api_version field
- SolveContext.from_dict() validates api_version, raises ValueError on
  mismatch
- Enums serialize as strings matching pybind11 .value() names
- Transform: {position: [x,y,z], quaternion: [w,x,y,z]}
- Optional simulation serializes as None/null
- Pure pybind11 py::dict construction, no new dependencies

Tests: 16 new tests in TestKCSolveSerialization covering round-trips for
all types, all 24 BaseJointKind values, all 4 SolveStatus values,
json.dumps/loads stdlib round-trip, and error cases (missing key,
invalid enum, bad array length, wrong api_version).
2026-02-20 11:58:18 -06:00
forbes
7ea0078ba3 feat(kcsolve): pybind11 bindings and Python solver support
All checks were successful
Build and Test / build (pull_request) Successful in 29m19s
Add the kcsolve pybind11 module exposing the KCSolve C++ API to Python:

- PyIKCSolver trampoline enabling Python IKCSolver subclasses
- Bindings for all 5 enums, 10 structs, IKCSolver, and OndselAdapter
- Module functions wrapping SolverRegistry (available, load, joints_for,
  set_default, get_default, register_solver)
- PySolverHolder class forwarding virtual calls with GIL acquisition
- register_solver() for runtime Python solver registration

IKCSolver constructor moved from protected to public for pybind11
trampoline access (class remains abstract via 3 pure virtuals).

Includes 16 Python tests covering module import, type bindings, enum
values, registry functions, Python solver subclassing, and full
register/load/solve round-trip.

Closes #288
2026-02-19 18:04:49 -06:00
forbes
934cdf5767 test(assembly): regression tests for KCSolve solver refactor (#296)
All checks were successful
Build and Test / build (pull_request) Successful in 29m11s
Phase 1e: Add C++ gtest and Python unittest coverage for the pluggable
solver system introduced in Phases 1a-1d.

C++ tests (KCSolve_tests_run):
- SolverRegistryTest (8 tests): register/get, duplicates, defaults,
  available list, joints_for capability queries
- OndselAdapterTest (10 tests): identity checks, supported/unsupported
  joints, Fixed/Revolute solve round-trips, no-grounded-parts handling,
  exception safety, drag protocol (pre_drag/drag_step/post_drag),
  redundant constraint diagnostics

Python tests (TestSolverIntegration):
- Full-stack solve via AssemblyObject → IKCSolver → OndselAdapter
- Fixed joint placement matching, revolute joint success
- No-ground error code (-6), redundancy detection (-2)
- ASMT export produces non-empty file
- Deterministic solve stability (solve twice → same result)
2026-02-19 16:56:11 -06:00
forbes
5c33aacecb feat(solver): refactor AssemblyObject to use IKCSolver interface (#295)
Rewire AssemblyObject to call through KCSolve::IKCSolver instead of
directly manipulating OndselSolver ASMT types.

Key changes:
- Remove all 30+ #include <OndselSolver/*> from AssemblyObject.cpp
- Remove MbDPartData, objectPartMap, mbdAssembly members
- Add solver_ (IKCSolver), lastResult_ (SolveResult), partIdToObjs_ maps
- New buildSolveContext() builds SolveContext from FreeCAD document objects
  with JointType/DistanceType -> BaseJointKind decomposition
- New computeMarkerTransform() replaces handleOneSideOfJoint()
- New computeRackPinionMarkers() replaces getRackPinionMarkers()
- Rewrite solve/preDrag/doDragStep/postDrag/generateSimulation to call
  through IKCSolver interface
- Rewrite setNewPlacements/validateNewPlacements to use SolveResult
- Rewrite updateSolveStatus to use ConstraintDiagnostic
- Add export_native() to IKCSolver for ASMT export support
- Register OndselAdapter at Assembly module init in AppAssembly.cpp
- Remove OndselSolver from Assembly_LIBS (linked transitively via KCSolve)

Assembly module now has zero OndselSolver includes. All solver coupling
is confined to KCSolve/OndselAdapter.cpp.
2026-02-19 16:43:52 -06:00
forbes
32dbe20ce0 feat(solver): implement OndselAdapter wrapping OndselSolver behind IKCSolver (#294)
Phase 1c of the pluggable solver system. Creates OndselAdapter — the
concrete IKCSolver implementation that wraps OndselSolver's Lagrangian
MBD engine behind the KCSolve abstraction layer.

The adapter translates KCSolve types (SolveContext, BaseJointKind,
Transform) to OndselSolver's ASMT hierarchy (ASMTAssembly, ASMTPart,
ASMTJoint, ASMTMarker) and extracts results back into SolveResult.
All 30+ OndselSolver #includes are confined to OndselAdapter.cpp.

Key implementation details:
- build_assembly(): creates ASMTAssembly from SolveContext
- create_joint(): maps 20 BaseJointKind values to ASMT joint types
  (eliminates the 35-case DistanceType switch — decomposition done upstream)
- Quaternion-to-rotation-matrix conversion for OndselSolver input
- Direct quaternion extraction for output (both use w,x,y,z convention)
- Drag lifecycle: pre_drag/drag_step/post_drag with stateful assembly
- Simulation lifecycle: run_kinematic/num_frames/update_for_frame
- Diagnostic extraction: iterates MBD system constraints for redundancy
- Static register_solver() for SolverRegistry integration
- ExternalSystem back-pointer NOT set — breaks bidirectional coupling

New files:
- Solver/OndselAdapter.h — class declaration with KCSolveExport
- Solver/OndselAdapter.cpp — full implementation (~530 lines)

Modified:
- Solver/CMakeLists.txt — add sources, link OndselSolver (PRIVATE)
2026-02-19 16:19:44 -06:00
forbes
76b91c6597 feat(solver): implement SolverRegistry with plugin loading (#293)
Phase 1b of the pluggable solver system. Converts KCSolve from a
header-only INTERFACE target to a SHARED library and implements
the SolverRegistry with dynamic plugin discovery.

Changes:
- Add KCSolveGlobal.h export macro header (KCSolveExport)
- Move SolverRegistry method bodies from header to SolverRegistry.cpp
- Implement scan() with dlopen/LoadLibrary plugin loading
- Add scan_default_paths() for KCSOLVE_PLUGIN_PATH + system paths
- Plugin entry points: kcsolve_api_version() + kcsolve_create()
- API version checking (major version compatibility)
- Convert CMakeLists.txt from INTERFACE to SHARED library
- Link FreeCADBase (PRIVATE) for Console logging
- Link dl on POSIX for dynamic loading
- Fix -Wmissing-field-initializers warnings in IKCSolver.h defaults

The registry discovers plugins by scanning directories for shared
libraries that export the kcsolve C entry points. Plugins are
validated for API version compatibility before registration.
Manual registration via register_solver() remains available for
built-in solvers (e.g. OndselAdapter in Phase 1c).
2026-02-19 16:07:37 -06:00
forbes
47e6c14461 feat(solver): define IKCSolver C++ API types and interface (#292)
Add the pluggable solver API as header-only files under
src/Mod/Assembly/Solver/. This is Phase 1a of the pluggable solver
system (INTER_SOLVER.md).

New files:
- Types.h: BaseJointKind enum (24 decomposed constraint types),
  Transform, Part, Constraint, SolveContext, SolveResult, and
  supporting types. Uses standalone types (no FreeCAD dependency)
  for future server worker compatibility.
- IKCSolver.h: Abstract solver interface with solve(), drag protocol
  (pre_drag/drag_step/post_drag), kinematic simulation
  (run_kinematic/num_frames/update_for_frame), and diagnostics.
  Only solve(), name(), and supported_joints() are pure virtual;
  all other methods have default implementations.
- SolverRegistry.h: Thread-safe singleton registry for solver
  backends with factory-based registration and default solver
  selection.
- CMakeLists.txt: INTERFACE library target (header-only for now).

Modified:
- Assembly/CMakeLists.txt: add_subdirectory(Solver)
- Assembly/App/CMakeLists.txt: link KCSolve INTERFACE target
2026-02-19 15:55:57 -06:00
0bc2cf3b6a Merge pull request 'feat(ztools): migrate to kindred_sdk palette system (#278)' (#283) from feat/ztools-sdk-migration into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #283
2026-02-19 20:48:37 +00:00
forbes
0330396843 refactor: extract theme from ztools into base distribution (#278)
All checks were successful
Build and Test / build (pull_request) Successful in 29m25s
- Add Spreadsheet color preferences to KindredCreate.cfg using FCText
  entries (TextColor, AliasedCellBackgroundColor, PositiveNumberColor,
  NegativeNumberColor) matching the C++ GetASCII() reader in SheetModel.cpp
- Remove CatppuccinMocha install directive from CMakeLists.txt
- Update ztools submodule: theme.py deleted, CatppuccinMocha preference
  pack removed, package.xml cleaned up

The previous apply_spreadsheet_colors() in ztools was a no-op: it called
SetUnsigned() but the Spreadsheet C++ reads GetASCII() — different param
types in FreeCAD's parameter system. Now properly fixed via preference pack.

Closes #278
2026-02-19 14:46:59 -06:00
forbes
0bc03ea421 feat: BOM auto-extraction and manifest field population (#276, #277)
All checks were successful
Build and Test / build (pull_request) Successful in 29m22s
Documentation updates:
- KNOWN_ISSUES.md: correct #6 (datum handling), resolve #10
  (delete_bom_entry) and #11 (silo icons), fix stale formatDate
  reference, mark completed next steps, add new next steps.
- INTEGRATION_PLAN.md: correct ztools SDK migration claim.

kc_format.py (#277):
- New _manifest_enrich_hook: populates part_uuid from SiloItemId and
  silo_instance from Silo API URL on every .kc save.
- New update_manifest_fields(): public API to update manifest fields
  in an already-saved .kc ZIP (used for post-upload revision_hash).

mods/silo submodule (#276):
- New bom_sync.py extraction engine.
- Post-commit hooks for BOM sync and manifest revision update.
- SSE bom_merged signal + Activity pane handler.
- merge_bom_json client method (forward-looking).

Refs: #276, #277
2026-02-19 12:37:24 -06:00
forbes
264e82179d feat(create): server integration for silo viewer widgets
All checks were successful
Build and Test / build (pull_request) Successful in 29m45s
Wire live data fetching, SSE subscriptions, and server write-back into
the History, Metadata, and Dependency viewer widgets.

Changes:
- Add server integration helpers (_init_server, _is_online,
  _get_part_number, offline banner) with lazy silo_commands import
- SiloHistoryViewer: Refresh button fetches live revisions via
  SiloClient.get_revisions(); SSE revision_created auto-refreshes
- SiloMetadataEditor: Save pushes to server (update_metadata,
  patch_lifecycle, patch_tags); SSE item_updated refreshes form
  when no local edits pending; offline banner
- SiloDependencyTable: Server-side UUID resolution via
  resolve_dependencies(); Download button for unresolved items;
  Refresh re-checks status; three-state icons (resolved/
  downloadable/missing)
- All viewers show 'Offline — showing cached data' banner when
  disconnected and disable server-dependent controls

Bump silo submodule to track new silo-client API methods:
  get_metadata, update_metadata, patch_lifecycle, patch_tags,
  resolve_dependencies (silo-client PR #19)

Closes kindred/silo-mod#43
2026-02-18 19:36:04 -06:00
forbes
9748384e7d art(create): placeholder tree-node icons for Silo viewer nodes (#42)
All checks were successful
Build and Test / build (pull_request) Successful in 29m26s
Add 10 SVG placeholder icons copied from existing silo action icons.
These provide immediate visual feedback in the document tree while
proper Catppuccin-themed icons are designed later.

Icons: silo-group, silo-manifest, silo-metadata, silo-history,
silo-approvals, silo-dependencies, silo-job, silo-macro,
silo-jobs-group, silo-macros-group

Also adds install(DIRECTORY) rule for resources/icons/ in CMakeLists.

Closes #42
2026-02-18 18:53:55 -06:00
forbes
bb14d7b0ef feat(create): remaining viewers — dependencies, jobs, macros, approvals (#41)
All checks were successful
Build and Test / build (pull_request) Successful in 31m3s
Add four viewer widgets for the remaining Silo tree node types:

- SiloApprovalsViewer: ECO approval status cards with colored status
  icons, state badge, and Open in Silo Web UI button
- SiloDependencyTable: QTableView with resolution status (checks open
  documents for matching part_uuid)
- SiloJobViewer: YAML source editor with Edit/Lock toggle, monospace
  font, dirty tracking, and unsaved-changes guard
- SiloMacroEditor: Python source editor with Run Now (exec in FreeCAD
  context), Save button, and dirty tracking

Also extends the viewer factory with prefix-based routing for
silo/jobs/*.yaml and silo/macros/*.py entries.

Closes #41
2026-02-18 18:46:06 -06:00
forbes
29f4a7b110 feat(create): history viewer — revision timeline display (#40)
All checks were successful
Build and Test / build (pull_request) Successful in 29m36s
Add SiloHistoryViewer widget that opens in an MDI subwindow when the
user double-clicks the History node in the Silo tree. Displays revision
cards newest-first with revision number, Catppuccin-themed lifecycle
status badges, author, timestamp, and commit comment.

Changes:
- silo_viewers.py: SiloHistoryViewer with revision card layout,
  status badge QSS, scroll area, empty-history placeholder

Closes #40
2026-02-18 18:41:56 -06:00
forbes
e947822c7a feat(create): metadata editor — editable form with dirty tracking and save-back (#39)
All checks were successful
Build and Test / build (pull_request) Successful in 30m48s
Add SiloMetadataEditor widget that opens in an MDI subwindow when the
user double-clicks the Metadata node in the Silo tree. Supports editing
lifecycle state, tags (add/remove chips), and schema-defined fields with
type-inferred widgets (QCheckBox, QDoubleSpinBox, QLineEdit).

Changes:
- silo_viewers.py: SiloMetadataEditor with dirty tracking, Save/Reset
  buttons, unsaved-changes close guard, and tag chip management
- silo_objects.py: mark_dirty()/is_dirty()/clear_dirty() on proxy
- kc_format.py: fix entries=None before hooks; _metadata_save_hook
  writes dirty RawContent back to silo/ cache on document save

Closes #39
2026-02-18 17:11:05 -06:00
forbes
90728414a9 feat(create): manifest viewer — read-only MDI widget for silo/manifest.json (#38)
All checks were successful
Build and Test / build (pull_request) Successful in 29m13s
Add SiloManifestViewer widget that opens in an MDI subwindow when the
user double-clicks the Manifest node in the Silo tree. Displays all
manifest.json fields in a read-only QFormLayout with copy buttons for
Part UUID and Silo Instance.

New files:
- silo_viewers.py: SiloManifestViewer widget + create_viewer_widget()
  factory with _VIEWER_REGISTRY for future viewer classes

Modified files:
- silo_viewproviders.py: doubleClicked() wired to open MDI subwindow
  with deduplication via widget objectName()
- CMakeLists.txt: add silo_viewers.py to install list

Closes #38
2026-02-18 16:48:34 -06:00
forbes
65f24b23eb feat(create): silo tree foundation for .kc files (#37)
All checks were successful
Build and Test / build (pull_request) Successful in 29m13s
Add document tree infrastructure that creates Silo metadata nodes when
a .kc file is opened. Nodes appear under a "Silo" group and represent
the silo/ ZIP directory entries (manifest, metadata, history, etc.).

New files:
- silo_objects.py: SiloViewerObject proxy with Transient properties
- silo_viewproviders.py: SiloViewerViewProvider with icon stubs
- silo_tree.py: SiloTreeBuilder with conditional node creation
- silo_document.py: SiloDocumentObserver singleton + registration

Modified files:
- kc_format.py: pre_reinject hook system for silo/ entry mutation
- InitGui.py: 600ms timer registration for document observer
- CMakeLists.txt: install list for 4 new Python files

Closes #37
2026-02-18 16:36:29 -06:00
forbes
99f2a92df4 fix(gui): make SVG icon rasterization DPI-aware in loadPixmap (#189)
loadPixmap() rendered all SVGs at a hardcoded 64x64 pixels regardless of
display DPI. On HiDPI screens, Qt then downscaled these low-resolution
pixmaps to the toolbar icon size, producing chunky/aliased icons.

Multiply the render size by getMaximumDPR() and tag the resulting pixmap
with the correct devicePixelRatio, matching the pattern already used in
pixmapFromSvg(const char* name, QSizeF size). This ensures SVGs are
rasterized at the physical resolution needed for crisp display.
2026-02-18 12:52:14 -06:00
5d8a253956 feat(create): add C++ module scaffold with App/ and Gui/ targets (#251)
Some checks failed
Build and Test / build (pull_request) Failing after 2m2s
Establish build infrastructure for Kindred-specific C++ features:

- CreateGlobal.h: export macros (CreateExport, CreateGuiExport)
- App/: CreateApp shared library (PyMOD_INIT_FUNC, Py::ExtensionModule)
- Gui/: CreateGui shared library (links CreateApp + FreeCADGui)
- CMakeLists.txt: add_subdirectory(App) + conditional add_subdirectory(Gui)

Scaffold only — no features. Follows the Assembly module pattern.
Existing Python Init.py/InitGui.py bootstrap unchanged.
2026-02-17 11:50:44 -06:00
64edae4c04 refactor: migrate ztools and Silo to kindred-addon-sdk (#250)
Some checks failed
Build and Test / build (pull_request) Failing after 1m51s
Route platform API calls through kindred_sdk wrappers:

ZTools:
- Replace hardcoded MOCHA dict with kindred_sdk.get_theme_tokens()
- Add sdk dependency to package.xml

Silo:
- Replace FreeCADGui.registerEditingOverlay() with kindred_sdk.register_overlay()
- Replace FreeCADGui.addOrigin()/removeOrigin() with kindred_sdk wrappers
- Replace hardcoded _MOCHA palette subset with kindred_sdk.get_theme_tokens()
- Add sdk dependency to package.xml

Create module:
- Replace dock panel boilerplate with kindred_sdk.register_dock_panel()

Behavior is identical before and after — this is a refactor only.

Closes #250
2026-02-17 08:59:56 -06:00
e667aceead feat(addon-system): create kindred-addon-sdk package (#249)
Some checks failed
Build and Test / build (pull_request) Failing after 1m40s
Add mods/sdk/ with the kindred_sdk Python package providing a stable
API layer for addon integration with Kindred Create platform features.

Modules:
- context: editing context/overlay registration wrappers
- theme: YAML-driven palette system (Catppuccin Mocha)
- origin: FileOrigin registration helpers
- dock: deferred dock panel registration
- compat: version detection utilities

The SDK loads at priority 0 (before all other addons) via the existing
manifest-driven loader. Theme colors are defined in a single YAML
palette file instead of hardcoded Python dicts, enabling future theme
support and eliminating color duplication across addons.

Closes #249
2026-02-17 08:36:27 -06:00
34964066a0 Merge pull request 'feat(addon-system): add <kindred> package.xml extensions and schema docs' (#257) from feat/package-xml-schema into main
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 46s
Build and Test / build (push) Failing after 2m1s
Reviewed-on: #257
2026-02-17 13:15:47 +00:00