Compare commits

...

71 Commits

Author SHA1 Message Date
forbes
a8fc1388ba docs(solver): server specification for KCSolve solver service
Comprehensive specification covering:
- Architecture: solver module integrated into Silo's job queue system
- Data model: JSON schemas for SolveContext and SolveResult transport
- REST API: submit, status, list, cancel endpoints under /api/solver/
- SSE events: solver.created, solver.progress, solver.completed, solver.failed
- Runner integration: standalone kcsolve execution, capability reporting
- Job definitions: manual solve, commit-time validation, kinematic simulation
- SolveContext extraction: headless Create and .kc archive packing
- Database schema: solver_results table with per-revision result caching
- Configuration: server and runner config patterns
- Security: input validation, runner isolation, authentication
- Client SDK: Python client and Create workbench integration sketches
- Implementation plan: Phase 3a-3e breakdown
2026-02-19 19:22:51 -06:00
forbes
bd43e62822 docs(kcsolve): expand Python API reference with full method docs
All checks were successful
Build and Test / build (pull_request) Successful in 29m49s
Expand SolveContext field descriptions (motions, simulation, bundle_fixed),
Constraint params table, marker explanations, Constraint.Limit descriptions,
MotionDef field descriptions, SimulationParams field descriptions, and all
optional IKCSolver methods with signatures, parameter docs, and usage
examples (interactive drag protocol, kinematic simulation, diagnostics,
export_native, capability queries).
2026-02-19 19:06:08 -06:00
forbes
406e120180 docs: KCSolve architecture and Python API reference
All checks were successful
Build and Test / build (pull_request) Successful in 28m58s
- Replace OndselSolver architecture doc with KCSolve pluggable solver
  architecture covering IKCSolver interface, SolverRegistry, OndselAdapter,
  Python bindings, file layout, and testing
- Add kcsolve Python API reference with full type documentation, module
  functions, usage examples, and pybind11 vector-copy caveat
- Add INTER_SOLVER.md spec (previously untracked) with Phase 1 and Phase 2
  marked as complete
- Update SUMMARY.md with new page links
2026-02-19 18:59:05 -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
f20ae3a667 Merge pull request 'feat(assembly): pluggable solver system — Phase 1 (#287)' (#297) from feat/solver-api-types into main
All checks were successful
Build and Test / build (push) Successful in 29m44s
Reviewed-on: #297
2026-02-19 22:58:33 +00: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
551979b441 Merge pull request 'chore: configure Kindred submodules to track main branch' (#286) from chore/submodule-branch-tracking into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #286
2026-02-19 20:58:27 +00:00
Kindred Bot
8897399a10 docs: sync Silo server documentation
Some checks failed
Build and Test / build (push) Has been cancelled
Deploy Docs / build-and-deploy (push) Successful in 51s
Auto-synced from kindred/silo main branch.
2026-02-19 20:58:03 +00:00
forbes
6dc4341a5f chore: configure Kindred submodules to track main branch
All checks were successful
Build and Test / build (pull_request) Successful in 29m19s
Add branch = main to mods/silo and mods/ztools in .gitmodules.
Enables 'git submodule update --remote' to auto-advance to latest main.
Third-party deps (GSL, googletest, AddonManager) remain pinned.
2026-02-19 14:57:31 -06:00
ab6d09c138 Merge pull request 'chore: update submodule pointers to latest main' (#285) from fix/submodule-pointers into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #285
2026-02-19 20:52:46 +00:00
forbes
133af52f11 chore: update submodule pointers to latest main
All checks were successful
Build and Test / build (pull_request) Successful in 29m12s
- mods/silo: f67d9a0 (feature branch) -> 43e905c (main, includes merged #49)
- mods/ztools: 296a94e (pre-rebase) -> 08e439b (rebased on main, includes theme removal)
2026-02-19 14:52:22 -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
6690d0355a chore: update mods/ztools pointer for SDK migration (#278)
All checks were successful
Build and Test / build (pull_request) Successful in 29m54s
Points to feat/sdk-migration branch with kindred_sdk palette integration.
2026-02-19 14:35:27 -06:00
7fe046379b Merge pull request 'feat: BOM auto-extraction from Assembly links + manifest field population' (#282) from feat/bom-sync-and-manifest into main
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 37s
Build and Test / build (push) Has been cancelled
Reviewed-on: #282
2026-02-19 20:23:22 +00: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
0c43957e9b Merge pull request 'chore: update mods/silo submodule pointer to main' (#275) from fix/silo-submodule-pointer into main
All checks were successful
Build and Test / build (push) Successful in 30m4s
Sync Silo Server Docs / sync (push) Successful in 41s
Reviewed-on: #275
2026-02-19 01:58:49 +00:00
forbes
2ce00a527a chore: update mods/silo pointer to main
All checks were successful
Build and Test / build (pull_request) Successful in 28m57s
Points to silo-mod main (af98994) which includes the correct
silo-client submodule pointer at main (285bd1f).
2026-02-18 19:58:30 -06:00
967e434607 Merge pull request 'feat(create): server integration for silo viewer widgets' (#274) from feat/server-integration into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #274
2026-02-19 01:41:59 +00: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
Kindred Bot
40fac46862 docs: sync Silo server documentation
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 34s
Build and Test / build (push) Has been cancelled
Auto-synced from kindred/silo main branch.
2026-02-19 01:13:03 +00:00
ed71a0c8b9 Merge pull request 'art(create): placeholder tree-node icons for Silo viewer nodes' (#273) from feat/silo-tree-icons into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #273
2026-02-19 01:11:45 +00:00
0ea2622a73 Merge pull request 'feat(create): remaining viewers — dependencies, jobs, macros, approvals' (#272) from feat/remaining-viewers into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #272
2026-02-19 00:54:24 +00: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
099d2a025a Merge pull request 'feat(create): history viewer — revision timeline display' (#271) from feat/history-viewer into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #271
2026-02-19 00:43:14 +00:00
3e93f4a756 Merge branch 'main' into feat/history-viewer
All checks were successful
Build and Test / build (pull_request) Successful in 29m35s
2026-02-19 00:43:03 +00:00
8b6205a340 Merge pull request 'feat(create): metadata editor — editable form with dirty tracking and save-back' (#270) from feat/metadata-editor into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #270
2026-02-19 00:42:54 +00:00
6fe5cc1d4d Merge branch 'main' into feat/metadata-editor
All checks were successful
Build and Test / build (pull_request) Successful in 29m56s
2026-02-19 00:42:45 +00: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
92183ef697 Merge pull request 'feat(create): manifest viewer — read-only MDI widget for silo/manifest.json' (#269) from feat/manifest-viewer into main
All checks were successful
Build and Test / build (push) Successful in 29m5s
Reviewed-on: #269
2026-02-18 22:50:32 +00:00
b721e67c8d Merge branch 'main' into feat/manifest-viewer
All checks were successful
Build and Test / build (pull_request) Successful in 29m6s
2026-02-18 22:50:16 +00: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
d87b79698f Merge pull request 'feat(create): silo tree foundation for .kc files' (#268) from feat/silo-tree-foundation into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #268
2026-02-18 22:38:22 +00: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
Kindred Bot
deb425db44 docs: sync Silo server documentation
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 34s
Build and Test / build (push) Successful in 28m56s
Auto-synced from kindred/silo main branch.
2026-02-18 21:05:53 +00:00
Kindred Bot
e70348508e docs: sync Silo server documentation
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 37s
Build and Test / build (push) Has been cancelled
Auto-synced from kindred/silo main branch.
2026-02-18 20:47:26 +00:00
41669eea8b Merge pull request 'fix(gui): make SVG icon rasterization DPI-aware in loadPixmap (#189)' (#266) from fix/toolbar-icon-dpi-scaling into main
All checks were successful
Build and Test / build (push) Successful in 29m59s
Reviewed-on: #266
2026-02-18 18:57:14 +00:00
forbes
ea49736549 test(gui): add BitmapFactory DPI-aware SVG rendering tests (#189)
All checks were successful
Build and Test / build (pull_request) Successful in 29m28s
Three tests for the loadPixmap() DPI fix:
- pixmapFromSvg(content, size) renders at the exact requested size
- getMaximumDPR() returns >= 1.0
- pixmap() loaded from an SVG file has correct devicePixelRatio and
  physical size matching 64 * DPR
2026-02-18 12:54:48 -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
4510ace7b9 Merge pull request 'fix(tests): add mods/sdk to sys.path in test_kindred_pure' (#265) from fix/test-sdk-path into main
All checks were successful
Build and Test / build (push) Successful in 30m40s
Sync Silo Server Docs / sync (push) Successful in 35s
Reviewed-on: #265
2026-02-17 18:53:24 +00:00
a1105c9d80 fix(tests): add mods/sdk to sys.path in test_kindred_pure
All checks were successful
Build and Test / build (pull_request) Successful in 28m20s
The SDK migration (#250) moved kindred_sdk into mods/sdk/ but the
test file was not updated with the new path, causing
ModuleNotFoundError when importing kindred_sdk.
2026-02-17 12:51:05 -06:00
06c698d425 Merge pull request 'docs: update architecture docs for addon-first model (#255)' (#264) from docs/update-architecture-for-addon-model into main
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 34s
Build and Test / build (push) Failing after 1m52s
Reviewed-on: #264
2026-02-17 18:46:03 +00:00
252e2c3b3e docs: update architecture docs for addon-first model (#255)
Some checks failed
Build and Test / build (pull_request) Failing after 2m1s
ARCHITECTURE.md:
- Replace exec()-based bootstrap flow with manifest-driven loader diagram
- Add addon lifecycle section (scan → parse → validate → resolve → load → register)
- Add SDK and C++ scaffold to source layout
- Document load order: sdk (0) → ztools (50) → silo (60)

INTEGRATION_PLAN.md:
- Add Layer 4 (Addon SDK) between bootstrap and addons (now 5 layers)
- Update Layer 3: now hosts both Python loader AND C++ scaffold
- Add Phase 1.5 (SDK) and Phase 1.75 (C++ scaffold) as DONE
- Update Phase 4 (theme): colors centralized in SDK YAML palette
- Update Phase 6 (build): now DONE with CMake install rules for all addons
- Add design decisions #7 (SDK as adaptation layer) and #8 (manifest-driven loading)

README.md:
- Update addon integration section: manifest-driven loading, SDK wrappers
- Add mods/sdk/ to project structure tree
- Update Create module description to mention C++ scaffold

Closes #255.
2026-02-17 12:38:18 -06:00
68380357fb Merge pull request 'docs: classify C++ patches by purpose and upstream-ability (#254)' (#263) from docs/classify-cpp-patches into main
Some checks failed
Deploy Docs / build-and-deploy (push) Successful in 35s
Build and Test / build (push) Failing after 1m53s
Reviewed-on: #263
2026-02-17 18:26:05 +00:00
076a1e90b0 docs: classify C++ patches by purpose and upstream-ability (#254)
Some checks failed
Build and Test / build (pull_request) Failing after 2m2s
Add Category column to Phase 2 cherry-pick table:
- 16 × Category 1 (Extension points) — KEEP
- 16 × Category 2 (Branding/theming) — KEEP, minimize
-  4 × Category 3 (Bug fixes) — UPSTREAM candidates

Add upstream verification for Category 3 patches:
- #26/#27 (Wayland scaling, menu icon size): partially upstream, menu pref not contributed
- #31 (window flickering): not upstream, good PR candidate
- #33 (ToolBarItem/reportException): Kindred-internal only

Update Assembly fixes with verified upstream status:
- All 4 patches still needed (joint flip, SIGSEGV, datum plane/point handling)
- App::Line handling fixed upstream via FreeCAD PR#20026

Closes #254.
2026-02-17 12:22:27 -06:00
ab2fde4755 Merge pull request 'feat(create): add C++ module scaffold with App/ and Gui/ targets (#251)' (#262) from feat/cpp-module-scaffold into main
Some checks failed
Build and Test / build (push) Failing after 1m57s
Reviewed-on: #262
2026-02-17 17:55:26 +00: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
6c11d64c5f Merge pull request 'fix: update silo submodule pointer after rebase onto main' (#261) from fix/silo-submodule-rebase into main
Some checks failed
Build and Test / build (push) Failing after 1m54s
Reviewed-on: #261
2026-02-17 17:05:51 +00:00
0ffa171c52 fix: update silo submodule pointer after rebase onto main
Some checks failed
Build and Test / build (pull_request) Failing after 2m2s
2026-02-17 11:04:18 -06:00
acfb627d67 Merge pull request 'refactor: migrate ztools and Silo to kindred-addon-sdk (#250)' (#260) from feat/migrate-addons-to-sdk into main
Some checks failed
Build and Test / build (push) Failing after 1m39s
Reviewed-on: #260
2026-02-17 15:50:58 +00: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
0ea1b1fde5 Merge pull request 'feat(addon-system): create kindred-addon-sdk package (#249)' (#259) from feat/kindred-addon-sdk into main
Some checks failed
Build and Test / build (push) Failing after 1m37s
Reviewed-on: #259
2026-02-17 14:49:32 +00: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
33026b6ff9 Merge pull request 'feat: addon registry with runtime introspection API (#253)' (#258) from feat/addon-registry-api into main
All checks were successful
Build and Test / build (push) Successful in 30m27s
Sync Silo Server Docs / sync (push) Successful in 50s
Reviewed-on: #258
2026-02-16 23:41:17 +00:00
forbes
9dd43a7cc3 feat: addon registry with runtime introspection API (#253)
All checks were successful
Build and Test / build (pull_request) Successful in 44m33s
Add FreeCAD.getAddonRegistry() function for runtime addon introspection.

Changes to addon_loader.py:
- Add contexts field to AddonManifest for tracking context IDs
- Add register_context() method for addons to declare contexts at runtime
- Add contexts() method returning {context_id: [addon_names]} mapping
- Parse <contexts> element from <kindred> in package.xml
- Add getAddonRegistry() function returning the registry singleton

Changes to Init.py:
- Expose getAddonRegistry as FreeCAD.getAddonRegistry after loading

Usage:
  registry = FreeCAD.getAddonRegistry()
  registry.get('ztools')        # AddonManifest for ztools
  registry.loaded()             # list of loaded addons
  registry.is_loaded('silo')    # True/False
  registry.contexts()           # {context_id: [addon_names]}

Closes #253
2026-02-16 17:33:28 -06:00
98d1877472 feat(addon-system): add <kindred> package.xml extensions and schema docs
Some checks failed
Build and Test / build (pull_request) Failing after 2m59s
Add <kindred> elements to ztools (priority=50) and silo (priority=60)
package.xml files declaring min_create_version, load_priority,
pure_python, and context metadata.

Fix addon_loader.py topological sort to use level-by-level processing
with (priority, name) sorting within each level, replacing
static_order() which did not guarantee deterministic ordering.

Add docs/src/development/package-xml-schema.md documenting the full
field reference, schema example, and backward compatibility notes.

Closes #252
2026-02-16 14:21:27 -06:00
587a95dd66 Merge pull request 'feat(bootstrap): replace exec()-based addon loading with manifest-driven loader' (#256) from feat/manifest-addon-loader into main
All checks were successful
Build and Test / build (push) Successful in 50m29s
Reviewed-on: #256
2026-02-16 19:17:59 +00:00
73f6641199 Merge pull request 'docs: extend README with architecture, platform support, and usage detail' (#247) from docs/extend-readme into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #247
2026-02-16 19:17:42 +00:00
60ceb47e4f feat(bootstrap): replace exec()-based addon loading with manifest-driven loader
All checks were successful
Build and Test / build (pull_request) Successful in 34m4s
Add addon_loader.py implementing a six-stage pipeline: scan mods/ for
package.xml files, parse standard fields and optional <kindred>
extensions via ElementTree, validate version compatibility, resolve
load order via graphlib.TopologicalSorter (with legacy fallback),
exec() Init.py/InitGui.py, and populate a runtime AddonRegistry
exposed as FreeCAD.KindredAddons.

Replace hard-coded addon lists in Init.py and InitGui.py with calls
to addon_loader.load_addons(). All QTimer-based Silo integration code
in InitGui.py is unchanged.

Backward compatible: addons without <kindred> elements load with no
constraints using the existing ztools-then-silo order.

Closes #248
2026-02-16 13:13:46 -06:00
a8df078eb3 docs: extend README with architecture, platform support, and usage detail
All checks were successful
Build and Test / build (pull_request) Successful in 35m8s
Add editing context system description, built-in context table, and
Python API reference. Add architecture section covering addon
integration and unified origin system. Expand ztools and Silo usage
sections with command breakdowns. Add platform support table. Expand
project structure tree with Kindred-specific files.
2026-02-16 11:02:11 -06:00
f4d91db094 Merge pull request 'feat(icons): add icon theming infrastructure with Catppuccin color remapping' (#246) from feat/icon-theming into main
Some checks failed
Build and Test / build (push) Has been cancelled
Deploy Docs / build-and-deploy (push) Successful in 46s
Sync Silo Server Docs / sync (push) Successful in 37s
Reviewed-on: #246
2026-02-16 02:36:17 +00:00
b083970a4d Merge branch 'main' into feat/icon-theming
Some checks failed
Build and Test / build (pull_request) Has been cancelled
2026-02-16 02:36:01 +00:00
forbes
d7b532255b feat(icons): add icon theming infrastructure with Catppuccin color remapping
Some checks failed
Build and Test / build (pull_request) Has been cancelled
- Remove hand-crafted kindred-icons/ in favor of auto-generated themed icons
- Add icons/mappings/ with FCAD.csv (Tango palette) and kindred.csv (Catppuccin Mocha)
- Add icons/retheme.py script to remap upstream FreeCAD SVG colors
- Generate icons/themed/ with 1,595 themed SVGs (45,300 color replacements)
- BitmapFactory loads icons/themed/ as highest priority before default icons
- 157-color mapping covers the full Tango palette, interpolating between
  4 luminance anchors per color family

Regenerate: python3 icons/retheme.py
2026-02-15 20:34:22 -06:00
19a91cb221 Merge pull request 'fix(assembly): guard onChanged against solver during document restore' (#245) from fix/assembly-restore-segfault into main
Some checks failed
Build and Test / build (push) Has been cancelled
Reviewed-on: #245
2026-02-16 00:46:17 +00:00
3144 changed files with 439300 additions and 11124 deletions

3
.gitignore vendored
View File

@@ -74,3 +74,6 @@ files_to_translate.txt
# mdBook build output
docs/book/
# To regenerate themed icons: python3 icons/retheme.py
# icons/themed/ is tracked (committed) so CI builds include them

2
.gitmodules vendored
View File

@@ -13,6 +13,8 @@
[submodule "mods/ztools"]
path = mods/ztools
url = https://git.kindred-systems.com/forbes/ztools.git
branch = main
[submodule "mods/silo"]
path = mods/silo
url = https://git.kindred-systems.com/kindred/silo-mod.git
branch = main

135
README.md

File diff suppressed because one or more lines are too long

View File

@@ -5,42 +5,116 @@
```
FreeCAD startup
└─ src/Mod/Create/Init.py
└─ setup_kindred_addons()
├─ exec(mods/ztools/ztools/Init.py)
exec(mods/silo/freecad/Init.py)
└─ addon_loader.load_addons(gui=False)
├─ scan_addons("mods/") — find package.xml manifests
parse_manifest() — extract <kindred> extensions
├─ validate_manifest() — check min/max_create_version
├─ resolve_load_order() — topological sort by <dependency>
└─ for each addon in order:
├─ add addon dir to sys.path
├─ exec(Init.py)
└─ register in AddonRegistry (FreeCAD.KindredAddons)
└─ src/Mod/Create/InitGui.py
├─ setup_kindred_workbenches()
exec(mods/ztools/ztools/InitGui.py)
└─ schedules deferred _register() (2000ms)
├─ imports ZTools commands
├─ installs _ZToolsManipulator (global)
└─ injects commands into editing contexts
└─ exec(mods/silo/freecad/InitGui.py)
├─ registers SiloWorkbench
└─ schedules deferred Silo overlay registration (2500ms)
├─ addon_loader.load_addons(gui=True)
for each addon in order:
└─ exec(InitGui.py)
├─ sdk (priority 0): logs "SDK loaded"
├─ ztools (priority 50): schedules deferred _register() (2000ms)
│ ├─ imports ZTools commands
│ ├─ installs _ZToolsManipulator (global)
│ └─ injects commands into editing contexts
└─ silo (priority 60): registers SiloWorkbench
│ └─ schedules deferred Silo overlay registration (2500ms)
├─ EditingContextResolver singleton created (MainWindow constructor)
│ ├─ registers built-in contexts (PartDesign, Sketcher, Assembly, Spreadsheet)
│ ├─ connects to signalInEdit/signalResetEdit/signalActiveDocument/signalActivateView
│ └─ BreadcrumbToolBar connected to contextChanged signal
└─ Deferred setup (QTimer):
├─ 500ms: _register_kc_format() → .kc file format
├─ 1500ms: _register_silo_origin() → registers Silo FileOrigin
├─ 2000ms: _setup_silo_auth_panel() → "Database Auth" dock
├─ 2000ms: ZTools _register() → commands + manipulator
├─ 2500ms: Silo overlay registration → "Silo Origin" toolbar overlay
├─ 3000ms: _check_silo_first_start() → settings prompt
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock (SSE)
├─ 4000ms: _setup_silo_activity_panel() → "Database Activity" dock
└─ 10000ms: _check_for_updates() → update checker (Gitea API)
```
### Addon lifecycle
Each addon in `mods/` provides a `package.xml` manifest with a `<kindred>` extension block:
```xml
<kindred>
<min_create_version>0.1.0</min_create_version>
<load_priority>50</load_priority>
<pure_python>true</pure_python>
<dependencies>
<dependency>sdk</dependency>
</dependencies>
</kindred>
```
The loader (`addon_loader.py`) processes addons in this order:
1. **Scan** — find all `mods/*/package.xml` files
2. **Parse** — extract `<kindred>` metadata (version bounds, priority, dependencies)
3. **Validate** — reject addons incompatible with the current Create version
4. **Resolve** — topological sort by `<dependency>` declarations, breaking ties by `<load_priority>`
5. **Load** — execute `Init.py` (console) then `InitGui.py` (GUI) for each addon
6. **Register** — populate `FreeCAD.KindredAddons` registry with addon state
Current load order: **sdk** (0) → **ztools** (50) → **silo** (60)
## Key source layout
```
src/Mod/Create/ Kindred bootstrap module (Python)
├── Init.py Adds mods/ addon paths, loads Init.py files
├── InitGui.py Loads workbenches, installs Silo manipulators
src/Mod/Create/ Kindred Create module
├── Init.py Console bootstrap — loads addons via manifest-driven loader
├── InitGui.py GUI bootstrap — loads addons, Silo integration, update checker
├── addon_loader.py Manifest-driven addon loader with dependency resolution
├── kc_format.py .kc file format round-trip preservation
├── version.py.in CMake template → version.py (build-time)
── update_checker.py Checks Gitea releases API for updates
── update_checker.py Checks Gitea releases API for updates
├── CreateGlobal.h C++ export macros (CreateExport, CreateGuiExport)
├── App/ C++ App library (CreateApp.so)
│ ├── AppCreate.cpp Module entry point — PyMOD_INIT_FUNC(CreateApp)
│ └── AppCreatePy.cpp Python module object (Py::ExtensionModule)
└── Gui/ C++ Gui library (CreateGui.so)
├── AppCreateGui.cpp Module entry point — PyMOD_INIT_FUNC(CreateGui)
└── AppCreateGuiPy.cpp Python module object (Py::ExtensionModule)
mods/sdk/ [dir] Kindred addon SDK — stable API contract
├── package.xml Manifest (priority 0, no dependencies)
├── kindred_sdk/
│ ├── __init__.py Public API re-exports
│ ├── context.py Editing context wrappers (register_context, register_overlay, ...)
│ ├── theme.py YAML-driven palette system (get_theme_tokens, load_palette, Palette)
│ ├── origin.py FileOrigin registration (register_origin, unregister_origin)
│ ├── dock.py Deferred dock panel helper (register_dock_panel)
│ ├── compat.py Version detection (create_version, freecad_version)
│ └── palettes/
│ └── catppuccin-mocha.yaml 26 colors + 14 semantic roles
└── Init.py / InitGui.py Minimal log messages
mods/ztools/ [submodule] command provider (not a workbench)
├── package.xml Manifest (priority 50, depends on sdk)
├── ztools/InitGui.py Deferred command registration + _ZToolsManipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons (SDK theme tokens), theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule → silo-mod.git] FreeCAD workbench
├── freecad/package.xml Manifest (priority 60, depends on sdk)
├── silo-client/ [submodule → silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench + overlay registration (via SDK)
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo (via SDK)
src/Gui/EditingContext.h/.cpp EditingContextResolver singleton + context registry
src/Gui/BreadcrumbToolBar.h/.cpp Color-coded breadcrumb toolbar (Catppuccin Mocha)
@@ -49,22 +123,6 @@ src/Gui/CommandOrigin.cpp Origin_Commit/Pull/Push/Info/BOM commands
src/Gui/OriginManager.h/.cpp Origin lifecycle management
src/Gui/OriginSelectorWidget.h/.cpp UI for origin selection
mods/ztools/ [submodule] command provider (not a workbench)
├── ztools/InitGui.py Deferred command registration + _ZToolsManipulator
├── ztools/ztools/
│ ├── commands/ Datum, pattern, pocket, assembly, spreadsheet
│ ├── datums/core.py Datum creation via Part::AttachExtension
│ └── resources/ Icons, theme utilities
└── CatppuccinMocha/ Theme preference pack (QSS)
mods/silo/ [submodule -> silo-mod.git] FreeCAD workbench
├── silo-client/ [submodule -> silo-client.git] shared API client
│ └── silo_client/ SiloClient, SiloSettings, CATEGORY_NAMES
└── freecad/ FreeCAD workbench (Python)
├── InitGui.py SiloWorkbench + Silo overlay context registration
├── silo_commands.py Commands + FreeCADSiloSettings adapter
└── silo_origin.py FileOrigin backend for Silo
src/Gui/Stylesheets/ QSS themes and SVG assets
src/Gui/PreferencePacks/ KindredCreate preference pack (cfg + build-time QSS)
```

View File

@@ -116,4 +116,4 @@ Notable theme customizations beyond standard Catppuccin colors:
### Palette
All silo-* icons use the Catppuccin Mocha color scheme. See `kindred-icons/README.md` for palette specification and icon design standards.
All silo-* icons use the Catppuccin Mocha color scheme. See `icons/kindred/README.md` for palette specification and icon design standards.

View File

@@ -0,0 +1,395 @@
# DAG Client Integration Contract
**Status:** Draft
**Last Updated:** 2026-02-13
This document describes what silo-mod and Headless Create runners need to implement to integrate with the Silo dependency DAG and worker system.
---
## 1. Overview
The DAG system has two client-side integration points:
1. **silo-mod workbench** (desktop) -- pushes DAG data to Silo on save or revision create.
2. **silorunner + silo-mod** (headless) -- extracts DAGs, validates features, and exports geometry as compute jobs.
Both share the same Python codebase in the silo-mod repository. Desktop users call the code interactively; runners call it headlessly via `create --console`.
---
## 2. DAG Sync Payload
Clients push feature trees to Silo via:
```
PUT /api/items/{partNumber}/dag
Authorization: Bearer <user_token or runner_token>
Content-Type: application/json
```
### 2.1 Request Body
```json
{
"revision_number": 3,
"nodes": [
{
"node_key": "Sketch001",
"node_type": "sketch",
"properties_hash": "a1b2c3d4e5f6...",
"metadata": {
"label": "Base Profile",
"constraint_count": 12
}
},
{
"node_key": "Pad001",
"node_type": "pad",
"properties_hash": "f6e5d4c3b2a1...",
"metadata": {
"label": "Main Extrusion",
"length": 25.0
}
}
],
"edges": [
{
"source_key": "Sketch001",
"target_key": "Pad001",
"edge_type": "depends_on"
}
]
}
```
### 2.2 Field Reference
**Nodes:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `node_key` | string | yes | Unique within item+revision. Use Create's internal object name (e.g. `Sketch001`, `Pad003`). |
| `node_type` | string | yes | One of: `sketch`, `pad`, `pocket`, `fillet`, `chamfer`, `constraint`, `body`, `part`, `datum`. |
| `properties_hash` | string | no | SHA-256 hex digest of the node's parametric inputs. Used for memoization. |
| `validation_state` | string | no | One of: `clean`, `dirty`, `validating`, `failed`. Defaults to `clean`. |
| `metadata` | object | no | Arbitrary key-value pairs for display or debugging. |
**Edges:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `source_key` | string | yes | The node that is depended upon. |
| `target_key` | string | yes | The node that depends on the source. |
| `edge_type` | string | no | One of: `depends_on` (default), `references`, `constrains`. |
**Direction convention:** Edges point from dependency to dependent. If Pad001 depends on Sketch001, the edge is `source_key: "Sketch001"`, `target_key: "Pad001"`.
### 2.3 Response
```json
{
"synced": true,
"node_count": 15,
"edge_count": 14
}
```
---
## 3. Computing properties_hash
The `properties_hash` enables memoization -- if a node's inputs haven't changed since the last validation, it can be skipped. Computing it:
```python
import hashlib
import json
def compute_properties_hash(feature_obj):
"""Hash the parametric inputs of a Create feature."""
inputs = {}
if feature_obj.TypeId == "Sketcher::SketchObject":
# Hash geometry + constraints
inputs["geometry_count"] = feature_obj.GeometryCount
inputs["constraint_count"] = feature_obj.ConstraintCount
inputs["geometry"] = str(feature_obj.Shape.exportBrep())
elif feature_obj.TypeId == "PartDesign::Pad":
inputs["length"] = feature_obj.Length.Value
inputs["type"] = str(feature_obj.Type)
inputs["reversed"] = feature_obj.Reversed
inputs["sketch"] = feature_obj.Profile[0].Name
# ... other feature types
canonical = json.dumps(inputs, sort_keys=True)
return hashlib.sha256(canonical.encode()).hexdigest()
```
The exact inputs per feature type are determined by what parametric values affect the feature's geometry. Include anything that, if changed, would require recomputation.
---
## 4. Feature Tree Walking
To extract the DAG from a Create document:
```python
import FreeCAD
def extract_dag(doc):
"""Walk a Create document and return nodes + edges."""
nodes = []
edges = []
for obj in doc.Objects:
# Skip non-feature objects
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
nodes.append({
"node_key": obj.Name,
"node_type": node_type,
"properties_hash": compute_properties_hash(obj),
"metadata": {
"label": obj.Label,
"type_id": obj.TypeId,
}
})
# Walk dependencies via InList (objects this one depends on)
for dep in obj.InList:
if hasattr(dep, "TypeId") and classify_type(dep.TypeId):
edges.append({
"source_key": dep.Name,
"target_key": obj.Name,
"edge_type": "depends_on",
})
return nodes, edges
def classify_type(type_id):
"""Map Create TypeIds to DAG node types."""
mapping = {
"Sketcher::SketchObject": "sketch",
"PartDesign::Pad": "pad",
"PartDesign::Pocket": "pocket",
"PartDesign::Fillet": "fillet",
"PartDesign::Chamfer": "chamfer",
"PartDesign::Body": "body",
"Part::Feature": "part",
"Sketcher::SketchConstraint": "constraint",
}
return mapping.get(type_id)
```
---
## 5. When to Push DAG Data
Push the DAG to Silo in these scenarios:
| Event | Trigger | Who |
|-------|---------|-----|
| User saves in silo-mod | On save callback | Desktop silo-mod workbench |
| User creates a revision | After `POST /api/items/{pn}/revisions` succeeds | Desktop silo-mod workbench |
| Runner extracts DAG | After `create-dag-extract` job completes | silorunner via `PUT /api/runner/jobs/{id}/dag` |
| Runner validates | After `create-validate` job, push updated validation states | silorunner via `PUT /api/runner/jobs/{id}/dag` |
---
## 6. Runner Entry Points
silo-mod must provide these Python entry points for headless invocation:
### 6.1 silo.runner.dag_extract
Extracts the feature DAG from a Create file and writes it as JSON.
```python
# silo/runner.py
def dag_extract(input_path, output_path):
"""
Extract feature DAG from a Create file.
Args:
input_path: Path to the .kc (Kindred Create) file.
output_path: Path to write the JSON output.
Output JSON format:
{
"nodes": [...], // Same format as DAG sync payload
"edges": [...]
}
"""
doc = FreeCAD.openDocument(input_path)
nodes, edges = extract_dag(doc)
with open(output_path, 'w') as f:
json.dump({"nodes": nodes, "edges": edges}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.2 silo.runner.validate
Rebuilds all features and reports pass/fail per node.
```python
def validate(input_path, output_path):
"""
Validate a Create file by rebuilding all features.
Output JSON format:
{
"valid": true/false,
"nodes": [
{
"node_key": "Pad001",
"state": "clean", // or "failed"
"message": null, // error message if failed
"properties_hash": "..."
}
]
}
"""
doc = FreeCAD.openDocument(input_path)
doc.recompute()
results = []
all_valid = True
for obj in doc.Objects:
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
state = "clean"
message = None
if hasattr(obj, "isValid") and not obj.isValid():
state = "failed"
message = f"Feature {obj.Label} failed to recompute"
all_valid = False
results.append({
"node_key": obj.Name,
"state": state,
"message": message,
"properties_hash": compute_properties_hash(obj),
})
with open(output_path, 'w') as f:
json.dump({"valid": all_valid, "nodes": results}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.3 silo.runner.export
Exports geometry to STEP, IGES, or other formats.
```python
def export(input_path, output_path, format="step"):
"""
Export a Create file to an external format.
Args:
input_path: Path to the .kc file.
output_path: Path to write the exported file.
format: Export format ("step", "iges", "stl", "obj").
"""
doc = FreeCAD.openDocument(input_path)
import Part
shapes = [obj.Shape for obj in doc.Objects if hasattr(obj, "Shape")]
compound = Part.makeCompound(shapes)
format_map = {
"step": "STEP",
"iges": "IGES",
"stl": "STL",
"obj": "OBJ",
}
Part.export([compound], output_path)
FreeCAD.closeDocument(doc.Name)
```
---
## 7. Headless Invocation
The `silorunner` binary shells out to Create (with silo-mod installed):
```bash
# DAG extraction
create --console -e "from silo.runner import dag_extract; dag_extract('/tmp/job/part.kc', '/tmp/job/dag.json')"
# Validation
create --console -e "from silo.runner import validate; validate('/tmp/job/part.kc', '/tmp/job/result.json')"
# Export
create --console -e "from silo.runner import export; export('/tmp/job/part.kc', '/tmp/job/output.step', 'step')"
```
**Prerequisites:** The runner host must have:
- Headless Create installed (Kindred's fork of FreeCAD)
- silo-mod installed as a Create addon (so `from silo.runner import ...` works)
- No display server required -- `--console` mode is headless
---
## 8. Validation Result Handling
After a runner completes a `create-validate` job, it should:
1. Read the result JSON.
2. Push updated validation states via `PUT /api/runner/jobs/{jobID}/dag`:
```json
{
"revision_number": 3,
"nodes": [
{"node_key": "Sketch001", "node_type": "sketch", "validation_state": "clean", "properties_hash": "abc..."},
{"node_key": "Pad001", "node_type": "pad", "validation_state": "failed", "properties_hash": "def..."}
],
"edges": [
{"source_key": "Sketch001", "target_key": "Pad001"}
]
}
```
3. Complete the job via `POST /api/runner/jobs/{jobID}/complete` with the summary result.
---
## 9. SSE Events
Clients should listen for these events on `GET /api/events`:
| Event | Payload | When |
|-------|---------|------|
| `dag.updated` | `{item_id, part_number, revision_number, node_count, edge_count}` | After any DAG sync |
| `dag.validated` | `{item_id, part_number, valid, failed_count}` | After validation completes |
| `job.created` | `{job_id, definition_name, trigger, item_id}` | Job auto-triggered or manually created |
| `job.claimed` | `{job_id, runner_id, runner}` | Runner claims a job |
| `job.progress` | `{job_id, progress, message}` | Runner reports progress |
| `job.completed` | `{job_id, runner_id}` | Job finishes successfully |
| `job.failed` | `{job_id, runner_id, error}` | Job fails |
| `job.cancelled` | `{job_id, cancelled_by}` | Job cancelled by user |
---
## 10. Cross-Item Edges
For assembly constraints that reference geometry in child parts (e.g. a mate constraint between two parts), use the `dag_cross_edges` table. These edges bridge the BOM DAG and the feature DAG.
Cross-item edges are **not** included in the standard `PUT /dag` sync. They will be managed through a dedicated endpoint in a future iteration once the assembly constraint model in Create/silo-mod is finalized.
For now, the DAG sync covers intra-item dependencies only. Assembly-level interference detection uses the BOM DAG (`relationships` table) combined with per-item feature DAGs.

File diff suppressed because one or more lines are too long

568
docs/INTER_SOLVER.md Normal file
View File

@@ -0,0 +1,568 @@
# Pluggable Assembly Solver Architecture
**Status:** Phase 2 complete
**Last Updated:** 2026-02-19
---
## 1. Problem
Kindred Create currently vendors OndselSolver as a monolithic assembly constraint solver. Different engineering domains benefit from different solver strategies — Lagrangian methods work well for rigid body assemblies but poorly for over-constrained or soft-constraint systems. A pluggable architecture lets us ship multiple solvers (including experimental ones) without touching core assembly logic, and lets the server farm out solve jobs to headless worker processes.
---
## 2. Design Goals
1. **Stable C++ API** — A solver-agnostic interface that the Assembly module calls. Solvers are shared libraries loaded at runtime.
2. **Python binding layer** — Every C++ solver is exposed to Python via pybind11, enabling rapid prototyping, debugging, and server-side execution without a full GUI build.
3. **Solver-defined joint types** — Each solver declares its own joint/mate vocabulary, mapped from a common base set (inspired by SOLIDWORKS mates: coincident, concentric, tangent, distance, angle, lock, etc.).
4. **Semi-deterministic solving** — Consistent results given consistent input ordering, with configurable tolerance and iteration limits.
5. **Server-compatible** — Solvers run as detached processes claimed by `silorunner` workers via the existing job queue.
---
## 3. Architecture Layers
```
┌──────────────────────────────────────────────────────┐
│ Layer 4: Server / Worker │
│ silorunner claims solve jobs, executes via Python │
│ Headless Create or standalone solver process │
├──────────────────────────────────────────────────────┤
│ Layer 3: Python Debug & Scripting │
│ pybind11 bindings for all solvers │
│ Introspection, step-through, constraint viz │
│ import kcsolve; s = kcsolve.load("ondsel") │
├──────────────────────────────────────────────────────┤
│ Layer 2: Solver Plugins (.so / .dll / .dylib) │
│ Each implements IKCSolver interface │
│ Registers joint types via manifest │
│ Loaded by SolverRegistry at runtime │
├──────────────────────────────────────────────────────┤
│ Layer 1: C++ Solver API (libkcsolve) │
│ IKCSolver, JointDef, SolveContext, SolveResult │
│ SolverRegistry (discovery, loading, selection) │
│ Ships as a shared library linked by Assembly module │
└──────────────────────────────────────────────────────┘
```
---
## 4. Layer 1: C++ Solver API
Located at `src/Mod/Assembly/Solver/` (or `src/Lib/KCSolve/` if we want it independent of Assembly).
### 4.1 Core Types
```cpp
namespace KCSolve {
// Unique identifier for a joint type within a solver
struct JointTypeId {
std::string solver_id; // e.g. "ondsel", "gnn", "relaxation"
std::string joint_name; // e.g. "coincident", "distance"
};
// Base joint categories (SOLIDWORKS-inspired vocabulary)
enum class BaseJointKind {
Coincident,
Concentric,
Tangent,
Distance,
Angle,
Lock,
Parallel,
Perpendicular,
PointOnLine,
SymmetricPlane,
Gear,
Rack,
Cam,
Slot,
Hinge,
Slider,
Cylindrical,
Planar,
Ball,
Screw,
Universal,
Custom // solver-specific extension
};
// A joint definition registered by a solver plugin
struct JointDef {
JointTypeId id;
BaseJointKind base_kind; // which vanilla category it maps to
std::string display_name;
std::string description;
uint32_t dof_removed; // degrees of freedom this joint removes
std::vector<std::string> params; // parameter names (e.g. "distance", "angle")
bool supports_limits = false;
bool supports_friction = false;
};
// A constraint instance in a solve problem
struct Constraint {
JointTypeId joint_type;
std::string part_a; // part label or id
std::string part_b;
// Geometry references (face, edge, vertex indices)
std::vector<std::string> refs_a;
std::vector<std::string> refs_b;
std::map<std::string, double> params; // param_name -> value
bool suppressed = false;
};
// Input to a solve operation
struct SolveContext {
std::vector<Constraint> constraints;
// Part placements as 4x4 transforms (initial guess)
std::map<std::string, std::array<double, 16>> placements;
// Which parts are grounded (fixed)
std::set<std::string> grounded;
// Solver config
double tolerance = 1e-10;
uint32_t max_iterations = 500;
bool deterministic = true; // force consistent ordering
// Optional: previous solution for warm-starting
std::map<std::string, std::array<double, 16>> warm_start;
};
enum class SolveStatus {
Converged,
MaxIterationsReached,
Overconstrained,
Underconstrained,
Redundant,
Failed
};
struct ConstraintDiagnostic {
std::string constraint_id;
double residual;
bool satisfied;
std::string message;
};
struct SolveResult {
SolveStatus status;
uint32_t iterations;
double final_residual;
double solve_time_ms;
std::map<std::string, std::array<double, 16>> placements;
std::vector<ConstraintDiagnostic> diagnostics;
// For semi-deterministic: hash of input ordering
uint64_t input_hash;
};
} // namespace KCSolve
```
### 4.2 Solver Interface
```cpp
namespace KCSolve {
class IKCSolver {
public:
virtual ~IKCSolver() = default;
// Identity
virtual std::string id() const = 0;
virtual std::string name() const = 0;
virtual std::string version() const = 0;
// Joint type registry — called once at load
virtual std::vector<JointDef> supported_joints() const = 0;
// Solve
virtual SolveResult solve(const SolveContext& ctx) = 0;
// Incremental: update a single constraint without full re-solve
// Default impl falls back to full solve
virtual SolveResult update(const SolveContext& ctx,
const std::string& changed_constraint) {
return solve(ctx);
}
// Diagnostic: check if a constraint set is well-posed before solving
virtual SolveStatus diagnose(const SolveContext& ctx) {
return SolveStatus::Converged; // optimistic default
}
// Determinism: given identical input, produce identical output
virtual bool is_deterministic() const { return false; }
};
// Plugin entry point — each .so exports this symbol
using CreateSolverFn = IKCSolver* (*)();
} // namespace KCSolve
```
### 4.3 Solver Registry
```cpp
namespace KCSolve {
class SolverRegistry {
public:
// Scan a directory for solver plugins (*.so / *.dll / *.dylib)
void scan(const std::filesystem::path& plugin_dir);
// Manual registration (for built-in solvers like Ondsel)
void register_solver(std::unique_ptr<IKCSolver> solver);
// Lookup
IKCSolver* get(const std::string& solver_id) const;
std::vector<std::string> available() const;
// Joint type resolution: find which solvers support a given base kind
std::vector<JointTypeId> joints_for(BaseJointKind kind) const;
// Global default solver
void set_default(const std::string& solver_id);
IKCSolver* get_default() const;
};
} // namespace KCSolve
```
### 4.4 Plugin Loading
Each solver plugin is a shared library exporting:
```cpp
extern "C" KCSolve::IKCSolver* kcsolve_create();
extern "C" const char* kcsolve_api_version(); // "1.0"
```
The registry `dlopen`s each library, checks `kcsolve_api_version()` compatibility, and calls `kcsolve_create()`. Plugins are discovered from:
1. `<install_prefix>/lib/kcsolve/` — system-installed solvers
2. `~/.config/KindredCreate/solvers/` — user-installed solvers
3. `KCSOLVE_PLUGIN_PATH` env var — development overrides
---
## 5. Layer 2: OndselSolver Adapter
The first plugin wraps the existing OndselSolver, mapping its internal constraint types to the `IKCSolver` interface.
```
src/Mod/Assembly/Solver/
├── IKCSolver.h # Interface + types from §4
├── SolverRegistry.cpp # Plugin discovery and loading
├── OndselAdapter.cpp # Wraps OndselSolver as IKCSolver plugin
└── CMakeLists.txt
```
`OndselAdapter` translates between `SolveContext` ↔ OndselSolver's Lagrangian formulation. This is the reference implementation and proves the API works before any new solvers are written.
Joint mapping for OndselAdapter:
| BaseJointKind | Ondsel Constraint | DOF Removed |
|---------------|-------------------|-------------|
| Coincident | PointOnPoint | 3 |
| Concentric | CylindricalOnCylindrical | 4 |
| Tangent | FaceOnFace (tangent mode) | 1 |
| Distance | PointOnPoint + offset | 2 |
| Angle | AxisAngle | 1 |
| Lock | FullLock | 6 |
| Hinge | RevoluteJoint | 5 |
| Slider | PrismaticJoint | 5 |
| Cylindrical | CylindricalJoint | 4 |
| Ball | SphericalJoint | 3 |
---
## 6. Layer 3: Python Bindings
### 6.1 pybind11 Module
```
src/Mod/Assembly/Solver/bindings/
├── kcsolve_py.cpp # pybind11 module definition
└── CMakeLists.txt
```
```python
import kcsolve
# List available solvers
print(kcsolve.available()) # ["ondsel", ...]
# Load a solver
solver = kcsolve.load("ondsel")
print(solver.name, solver.version)
print(solver.supported_joints())
# Build a problem
ctx = kcsolve.SolveContext()
ctx.add_part("base", placement=..., grounded=True)
ctx.add_part("arm", placement=...)
ctx.add_constraint("coincident", "base", "arm",
refs_a=["Face6"], refs_b=["Face1"])
# Solve
result = solver.solve(ctx)
print(result.status) # SolveStatus.Converged
print(result.iterations) # 12
print(result.solve_time_ms) # 3.4
print(result.placements["arm"])
# Diagnostics per constraint
for d in result.diagnostics:
print(f"{d.constraint_id}: residual={d.residual:.2e} ok={d.satisfied}")
```
### 6.2 Debug / Introspection API
The Python layer adds capabilities the C++ interface intentionally omits for performance:
```python
# Step-through solving (debug mode)
debugger = kcsolve.Debugger(solver, ctx)
for step in debugger.iterate():
print(f"iter {step.iteration}: residual={step.residual:.6e}")
print(f" moved: {step.parts_moved}")
print(f" worst constraint: {step.worst_constraint}")
if step.residual < 1e-8:
break
# Constraint dependency graph
graph = kcsolve.dependency_graph(ctx)
# Returns dict: constraint_id -> [dependent_constraint_ids]
# DOF analysis
analysis = kcsolve.dof_analysis(ctx)
print(f"Total DOF: {analysis.total_dof}")
print(f"Removed: {analysis.constrained_dof}")
print(f"Remaining: {analysis.free_dof}")
for part, dofs in analysis.per_part.items():
print(f" {part}: {dofs} free")
```
### 6.3 Pure-Python Solver Support
The Python layer also supports solvers written entirely in Python (no C++ required). This is the fast path for prototyping new approaches (GNN, relaxation, etc.):
```python
class RelaxationSolver(kcsolve.PySolver):
"""A pure-Python iterative relaxation solver for prototyping."""
id = "relaxation"
name = "Iterative Relaxation"
version = "0.1.0"
def supported_joints(self):
return [
kcsolve.JointDef("coincident", kcsolve.BaseJointKind.Coincident, dof_removed=3),
kcsolve.JointDef("distance", kcsolve.BaseJointKind.Distance, dof_removed=2),
# ...
]
def solve(self, ctx: kcsolve.SolveContext) -> kcsolve.SolveResult:
placements = dict(ctx.placements)
for i in range(ctx.max_iterations):
max_residual = 0.0
for c in ctx.constraints:
residual = self._eval_constraint(c, placements)
correction = self._compute_correction(c, residual)
self._apply_correction(placements, c, correction)
max_residual = max(max_residual, abs(residual))
if max_residual < ctx.tolerance:
return kcsolve.SolveResult(
status=kcsolve.SolveStatus.Converged,
iterations=i + 1,
final_residual=max_residual,
placements=placements
)
return kcsolve.SolveResult(
status=kcsolve.SolveStatus.MaxIterationsReached,
iterations=ctx.max_iterations,
final_residual=max_residual,
placements=placements
)
# Register at runtime
kcsolve.register(RelaxationSolver())
```
Python solvers are discovered from:
- `<user_macros>/solvers/*.py` — user-written solvers
- `mods/*/solvers/*.py` — addon-provided solvers
---
## 7. Layer 4: Server Integration
### 7.1 Solve Job Definition
Extends the existing worker system (WORKERS.md) with a new job type:
```yaml
job:
name: assembly-solve
version: 1
description: "Solve assembly constraints using specified solver"
trigger:
type: revision_created
filter:
item_type: assembly
scope:
type: assembly
compute:
type: solve
command: create-solve
args:
solver: ondsel # or "auto" for registry default
tolerance: 1e-10
max_iterations: 500
deterministic: true
output_placements: true # write solved placements back to revision
output_diagnostics: true # store constraint diagnostics in job result
runner:
tags: [create, solver]
timeout: 300
max_retries: 1
priority: 75
```
### 7.2 Headless Solve via Runner
The `create-solve` command in `silorunner`:
1. Claims job from Silo server
2. Downloads the assembly `.kc` file
3. Launches Headless Create (or standalone Python if pure-Python solver)
4. Loads the assembly, extracts constraint graph → `SolveContext`
5. Calls `solver.solve(ctx)`
6. Reports `SolveResult` back via `POST /api/runner/jobs/{id}/complete`
7. Optionally writes updated placements as a new revision
### 7.3 Standalone Solve Process (No GUI)
For server-side batch solving without Headless Create overhead:
```python
#!/usr/bin/env python3
"""Standalone solver worker — no FreeCAD dependency."""
import kcsolve
import json, sys
problem = json.load(sys.stdin)
ctx = kcsolve.SolveContext.from_dict(problem)
solver = kcsolve.load(problem.get("solver", "ondsel"))
result = solver.solve(ctx)
json.dump(result.to_dict(), sys.stdout)
```
This enables lightweight solver containers that don't need the full Create installation — useful for CI validation, quick constraint checks, and scaling solver capacity independently of geometry workers.
---
## 8. Semi-Deterministic Behavior
"Semi-deterministic" means: given the same constraint set and initial placements, the solver produces the same result. This is achieved by:
1. **Canonical input ordering**`SolveContext` sorts constraints and parts by a stable key (part label + constraint index) before passing to the solver. The ordering hash is stored in `SolveResult.input_hash`.
2. **Solver contract**`IKCSolver::is_deterministic()` reports whether the implementation guarantees this. OndselAdapter does (Lagrangian formulation with fixed pivot ordering). A GNN solver might not.
3. **Tolerance-aware comparison** — Two `SolveResult`s are "equivalent" if all placement deltas are within tolerance, even if iteration counts differ. Used for regression testing.
4. **Warm-start stability** — When `warm_start` placements are provided, the solver should converge to the same solution as a cold start (within tolerance), just faster. This is validated in the test suite.
---
## 9. Implementation Phases
### Phase 1: API + OndselAdapter (foundation) -- COMPLETE
- Defined `IKCSolver.h`, core types (`Types.h`), `SolverRegistry`
- Implemented `OndselAdapter` wrapping existing solver
- Assembly module calls through `SolverRegistry` instead of directly calling OndselSolver
- 18 C++ tests, 6 Python integration tests
- **PR:** #297 (merged)
### Phase 2: pybind11 Bindings -- COMPLETE
- Built `kcsolve` pybind11 module exposing all enums, structs, and classes
- `PyIKCSolver` trampoline for pure-Python solver subclasses
- `register_solver()` for runtime Python solver registration
- `PySolverHolder` for GIL-safe forwarding of virtual calls
- 16 Python tests covering types, registry, and Python solver round-trips
- Debug/introspection API (Debugger, `dependency_graph()`, `dof_analysis()`) deferred to Phase 4+
- Automatic Python solver discovery (`mods/*/solvers/`) deferred -- users call `register_solver()` explicitly
- **PR:** #298
- **Docs:** `docs/src/architecture/ondsel-solver.md`, `docs/src/reference/kcsolve-python.md`
### Phase 3: Server Integration
- `create-solve` command for `silorunner`
- YAML job definition for solve jobs
- Standalone solver process (no FreeCAD dependency)
- `SolveContext` JSON serialization for inter-process communication
- **Deliverable:** Solve jobs run async through the worker system
### Phase 4: Second Solver (validation)
- Implement a simple relaxation or gradient-descent solver as a Python plugin
- Validates that the API actually supports different solving strategies
- Benchmark against OndselAdapter for correctness and performance
- **Deliverable:** Two interchangeable solvers, selectable per-assembly
### Phase 5: GNN Solver (future)
- Graph Neural Network approach from existing roadmap
- Likely a Python solver wrapping a trained model
- Focus on fast approximate solutions for interactive editing
- Falls back to OndselAdapter for final precision solve
- **Deliverable:** Hybrid solve pipeline (GNN fast-guess → Lagrangian refinement)
---
## 10. File Locations
```
src/Lib/KCSolve/ # or src/Mod/Assembly/Solver/
├── include/
│ └── KCSolve/
│ ├── IKCSolver.h # Interface + all types
│ ├── SolverRegistry.h # Plugin loading and lookup
│ └── Types.h # Enums, structs
├── src/
│ ├── SolverRegistry.cpp
│ └── OndselAdapter.cpp
├── bindings/
│ └── kcsolve_py.cpp # pybind11
├── plugins/ # Additional compiled solver plugins
└── CMakeLists.txt
```
---
## 11. Open Questions
1. **Location**: `src/Lib/KCSolve/` (independent library, usable without Assembly module) vs `src/Mod/Assembly/Solver/` (tighter coupling, simpler build)? Leaning toward `src/Lib/` since server workers need it without the full Assembly module.
2. **Geometry abstraction**: The C++ API uses string references for faces/edges/vertices. Should we pass actual OCC geometry (TopoDS_Shape) through the interface, or keep it abstract and let each solver adapter resolve references? Abstract is more portable but adds a translation step.
3. **Constraint persistence**: Currently constraints live in the FCStd XML. Should the pluggable layer introduce its own serialization, or always read/write through FreeCAD's property system?
4. **API versioning**: `kcsolve_api_version()` returns a string. Semver with major-only breaking changes? How strict on backward compat for the plugin ABI?
5. **License implications**: OndselSolver is LGPL. New solver plugins could be any license since they're loaded at runtime via a stable C API boundary. Confirm this interpretation.
---
## 12. References
- [ondsel-solver.md](ondsel-solver.md) — Current solver documentation
- [WORKERS.md](WORKERS.md) — Worker/runner job system
- [MULTI_USER_EDITS.md](MULTI_USER_EDITS.md) — Async validation pipeline
- [DAG.md](DAG.md) — Dependency graph for incremental recompute
- [ROADMAP.md](ROADMAP.md) — Tier 3 compute modules, GNN solver plans

View File

@@ -468,7 +468,7 @@ The `kc_version` field in `silo/manifest.json` tracks the schema version. When t
| MIME type | `application/x-kindred-create` |
| UTI (macOS) | `com.kindred-systems.create.document` |
| Magic bytes | `PK` (ZIP header, offset 0) + `silo/manifest.json` entry present |
| Icon | Kindred Create document icon (from `kindred-icons/` asset set) |
| Icon | Kindred Create document icon (from `icons/kindred/` asset set) |
---

View File

@@ -16,7 +16,7 @@
5. **No unit tests.** Zero test coverage for ztools and Silo FreeCAD commands. Silo Go backend also lacks tests.
6. **Assembly solver datum handling is minimal.** The `findPlacement()` fix in `src/Mod/Assembly/UtilsAssembly.py` extracts placement from `obj.Shape.Faces[0]` for `PartDesign::Plane` and from shape vertex for `PartDesign::Point`. Does not handle empty shapes or non-planar datum objects.
6. **Assembly solver datum handling is minimal.** `UtilsAssembly.findPlacement()` handles standard shapes (faces, edges, vertices) and `App::Line` origin objects. It does not extract placement from `PartDesign::Plane` or `PartDesign::Point` datum objects — when no element is selected, it returns a default `App.Placement()`. This means assembly joints referencing datum planes/points may produce incorrect placement.
### Medium
@@ -26,9 +26,9 @@
9. **tangent_to_cylinder falls back to manual placement.** TangentPlane MapMode requires a vertex reference not collected by the current UI.
10. **`delete_bom_entry()` bypasses error normalization.** Uses raw `urllib.request` instead of `SiloClient._request()`.
10. ~~**`delete_bom_entry()` bypasses error normalization.**~~ Resolved. `delete_bom_entry()` uses `self._request("DELETE", ...)` which routes through `SiloClient._request()` with proper error handling.
11. **Missing Silo icons.** Three commands reference icons that don't exist: `silo-tag.svg` (`Silo_TagProjects`), `silo-rollback.svg` (`Silo_Rollback`), `silo-status.svg` (`Silo_SetStatus`). The `_icon()` function returns an empty string, so these commands render without toolbar icons.
11. ~~**Missing Silo icons.**~~ Resolved. All three icons now exist: `silo-tag.svg`, `silo-rollback.svg`, `silo-status.svg` in `mods/silo/freecad/resources/icons/`.
### Fixed (retain for reference)
@@ -50,7 +50,7 @@
| CSRF protection | Implemented | `nosurf` library on web form routes |
| File locking | Not implemented | Needed to prevent concurrent edits |
| Odoo ERP integration | Stub only | Returns "not yet implemented" |
| Part number date segments | Broken | `formatDate()` returns error |
| Part number date segments | Unknown | `formatDate()` reference is stale — function not found in codebase |
| Location/inventory APIs | Tables exist, no handlers | |
| CSV import rollback | Not implemented | `bom_handlers.go` |
| SSE event streaming | Implemented | Reconnect logic with exponential backoff |
@@ -71,14 +71,20 @@
1. **Authentication hardening** -- Deploy FreeIPA and Keycloak infrastructure. End-to-end test LDAP and OIDC flows. Harden token rotation and session expiry.
2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save.
2. **BOM-Assembly bridge** -- Auto-populate Silo BOM from Assembly component links on save. See `docs/BOM_MERGE.md` for specification.
3. **File locking** -- Pessimistic locks on `Silo_Open` to prevent concurrent edits. Requires server-side lock table and client-side lock display.
4. **Build system** -- CMake install rules for `mods/` submodules so packages include ztools and Silo without manual steps.
4. ~~**Build system**~~ Done. CMake install rules in `src/Mod/Create/CMakeLists.txt` handle all `mods/` submodules.
5. **Test coverage** -- Unit tests for ztools datum creation, Silo FreeCAD commands, and Go API endpoints.
6. **QSS consolidation** -- Eliminate the 3-copy QSS duplication via build-time copy or symlinks. The canonical source is `resources/preferences/KindredCreate/KindredCreate.qss`.
6. ~~**QSS consolidation**~~ Done. Canonical QSS is `src/Gui/Stylesheets/KindredCreate.qss`; PreferencePacks copy generated at build time via `configure_file()`.
7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend is already implemented.
7. **Update notification UI** -- Display in-app notification when a new release is available (issue #30). The update checker backend (`update_checker.py`) runs at startup; notification UI still needed.
8. **KC file format completion** -- Populate `silo_instance` and `revision_hash` in manifest.json. Implement write-back for history.json, approvals.json, dependencies.json. See `docs/KC_SPECIFICATION.md`.
9. **ztools SDK migration** -- Add `<kindred>` metadata to `mods/ztools/package.xml` (load priority, version bounds, SDK dependency). Migrate `InitGui.py` to use `kindred_sdk` APIs for context/overlay registration.
10. **DAG cross-item edges** -- Assembly constraints referencing geometry in child parts should populate `dag_cross_edges`. Deferred until assembly constraint model is finalized.

395
docs/MULTI_USER_CLIENT.md Normal file
View File

@@ -0,0 +1,395 @@
# DAG Client Integration Contract
**Status:** Draft
**Last Updated:** 2026-02-13
This document describes what silo-mod and Headless Create runners need to implement to integrate with the Silo dependency DAG and worker system.
---
## 1. Overview
The DAG system has two client-side integration points:
1. **silo-mod workbench** (desktop) -- pushes DAG data to Silo on save or revision create.
2. **silorunner + silo-mod** (headless) -- extracts DAGs, validates features, and exports geometry as compute jobs.
Both share the same Python codebase in the silo-mod repository. Desktop users call the code interactively; runners call it headlessly via `create --console`.
---
## 2. DAG Sync Payload
Clients push feature trees to Silo via:
```
PUT /api/items/{partNumber}/dag
Authorization: Bearer <user_token or runner_token>
Content-Type: application/json
```
### 2.1 Request Body
```json
{
"revision_number": 3,
"nodes": [
{
"node_key": "Sketch001",
"node_type": "sketch",
"properties_hash": "a1b2c3d4e5f6...",
"metadata": {
"label": "Base Profile",
"constraint_count": 12
}
},
{
"node_key": "Pad001",
"node_type": "pad",
"properties_hash": "f6e5d4c3b2a1...",
"metadata": {
"label": "Main Extrusion",
"length": 25.0
}
}
],
"edges": [
{
"source_key": "Sketch001",
"target_key": "Pad001",
"edge_type": "depends_on"
}
]
}
```
### 2.2 Field Reference
**Nodes:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `node_key` | string | yes | Unique within item+revision. Use Create's internal object name (e.g. `Sketch001`, `Pad003`). |
| `node_type` | string | yes | One of: `sketch`, `pad`, `pocket`, `fillet`, `chamfer`, `constraint`, `body`, `part`, `datum`. |
| `properties_hash` | string | no | SHA-256 hex digest of the node's parametric inputs. Used for memoization. |
| `validation_state` | string | no | One of: `clean`, `dirty`, `validating`, `failed`. Defaults to `clean`. |
| `metadata` | object | no | Arbitrary key-value pairs for display or debugging. |
**Edges:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `source_key` | string | yes | The node that is depended upon. |
| `target_key` | string | yes | The node that depends on the source. |
| `edge_type` | string | no | One of: `depends_on` (default), `references`, `constrains`. |
**Direction convention:** Edges point from dependency to dependent. If Pad001 depends on Sketch001, the edge is `source_key: "Sketch001"`, `target_key: "Pad001"`.
### 2.3 Response
```json
{
"synced": true,
"node_count": 15,
"edge_count": 14
}
```
---
## 3. Computing properties_hash
The `properties_hash` enables memoization -- if a node's inputs haven't changed since the last validation, it can be skipped. Computing it:
```python
import hashlib
import json
def compute_properties_hash(feature_obj):
"""Hash the parametric inputs of a Create feature."""
inputs = {}
if feature_obj.TypeId == "Sketcher::SketchObject":
# Hash geometry + constraints
inputs["geometry_count"] = feature_obj.GeometryCount
inputs["constraint_count"] = feature_obj.ConstraintCount
inputs["geometry"] = str(feature_obj.Shape.exportBrep())
elif feature_obj.TypeId == "PartDesign::Pad":
inputs["length"] = feature_obj.Length.Value
inputs["type"] = str(feature_obj.Type)
inputs["reversed"] = feature_obj.Reversed
inputs["sketch"] = feature_obj.Profile[0].Name
# ... other feature types
canonical = json.dumps(inputs, sort_keys=True)
return hashlib.sha256(canonical.encode()).hexdigest()
```
The exact inputs per feature type are determined by what parametric values affect the feature's geometry. Include anything that, if changed, would require recomputation.
---
## 4. Feature Tree Walking
To extract the DAG from a Create document:
```python
import FreeCAD
def extract_dag(doc):
"""Walk a Create document and return nodes + edges."""
nodes = []
edges = []
for obj in doc.Objects:
# Skip non-feature objects
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
nodes.append({
"node_key": obj.Name,
"node_type": node_type,
"properties_hash": compute_properties_hash(obj),
"metadata": {
"label": obj.Label,
"type_id": obj.TypeId,
}
})
# Walk dependencies via InList (objects this one depends on)
for dep in obj.InList:
if hasattr(dep, "TypeId") and classify_type(dep.TypeId):
edges.append({
"source_key": dep.Name,
"target_key": obj.Name,
"edge_type": "depends_on",
})
return nodes, edges
def classify_type(type_id):
"""Map Create TypeIds to DAG node types."""
mapping = {
"Sketcher::SketchObject": "sketch",
"PartDesign::Pad": "pad",
"PartDesign::Pocket": "pocket",
"PartDesign::Fillet": "fillet",
"PartDesign::Chamfer": "chamfer",
"PartDesign::Body": "body",
"Part::Feature": "part",
"Sketcher::SketchConstraint": "constraint",
}
return mapping.get(type_id)
```
---
## 5. When to Push DAG Data
Push the DAG to Silo in these scenarios:
| Event | Trigger | Who |
|-------|---------|-----|
| User saves in silo-mod | On save callback | Desktop silo-mod workbench |
| User creates a revision | After `POST /api/items/{pn}/revisions` succeeds | Desktop silo-mod workbench |
| Runner extracts DAG | After `create-dag-extract` job completes | silorunner via `PUT /api/runner/jobs/{id}/dag` |
| Runner validates | After `create-validate` job, push updated validation states | silorunner via `PUT /api/runner/jobs/{id}/dag` |
---
## 6. Runner Entry Points
silo-mod must provide these Python entry points for headless invocation:
### 6.1 silo.runner.dag_extract
Extracts the feature DAG from a Create file and writes it as JSON.
```python
# silo/runner.py
def dag_extract(input_path, output_path):
"""
Extract feature DAG from a Create file.
Args:
input_path: Path to the .kc (Kindred Create) file.
output_path: Path to write the JSON output.
Output JSON format:
{
"nodes": [...], // Same format as DAG sync payload
"edges": [...]
}
"""
doc = FreeCAD.openDocument(input_path)
nodes, edges = extract_dag(doc)
with open(output_path, 'w') as f:
json.dump({"nodes": nodes, "edges": edges}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.2 silo.runner.validate
Rebuilds all features and reports pass/fail per node.
```python
def validate(input_path, output_path):
"""
Validate a Create file by rebuilding all features.
Output JSON format:
{
"valid": true/false,
"nodes": [
{
"node_key": "Pad001",
"state": "clean", // or "failed"
"message": null, // error message if failed
"properties_hash": "..."
}
]
}
"""
doc = FreeCAD.openDocument(input_path)
doc.recompute()
results = []
all_valid = True
for obj in doc.Objects:
if not hasattr(obj, "TypeId"):
continue
node_type = classify_type(obj.TypeId)
if node_type is None:
continue
state = "clean"
message = None
if hasattr(obj, "isValid") and not obj.isValid():
state = "failed"
message = f"Feature {obj.Label} failed to recompute"
all_valid = False
results.append({
"node_key": obj.Name,
"state": state,
"message": message,
"properties_hash": compute_properties_hash(obj),
})
with open(output_path, 'w') as f:
json.dump({"valid": all_valid, "nodes": results}, f)
FreeCAD.closeDocument(doc.Name)
```
### 6.3 silo.runner.export
Exports geometry to STEP, IGES, or other formats.
```python
def export(input_path, output_path, format="step"):
"""
Export a Create file to an external format.
Args:
input_path: Path to the .kc file.
output_path: Path to write the exported file.
format: Export format ("step", "iges", "stl", "obj").
"""
doc = FreeCAD.openDocument(input_path)
import Part
shapes = [obj.Shape for obj in doc.Objects if hasattr(obj, "Shape")]
compound = Part.makeCompound(shapes)
format_map = {
"step": "STEP",
"iges": "IGES",
"stl": "STL",
"obj": "OBJ",
}
Part.export([compound], output_path)
FreeCAD.closeDocument(doc.Name)
```
---
## 7. Headless Invocation
The `silorunner` binary shells out to Create (with silo-mod installed):
```bash
# DAG extraction
create --console -e "from silo.runner import dag_extract; dag_extract('/tmp/job/part.kc', '/tmp/job/dag.json')"
# Validation
create --console -e "from silo.runner import validate; validate('/tmp/job/part.kc', '/tmp/job/result.json')"
# Export
create --console -e "from silo.runner import export; export('/tmp/job/part.kc', '/tmp/job/output.step', 'step')"
```
**Prerequisites:** The runner host must have:
- Headless Create installed (Kindred's fork of FreeCAD)
- silo-mod installed as a Create addon (so `from silo.runner import ...` works)
- No display server required -- `--console` mode is headless
---
## 8. Validation Result Handling
After a runner completes a `create-validate` job, it should:
1. Read the result JSON.
2. Push updated validation states via `PUT /api/runner/jobs/{jobID}/dag`:
```json
{
"revision_number": 3,
"nodes": [
{"node_key": "Sketch001", "node_type": "sketch", "validation_state": "clean", "properties_hash": "abc..."},
{"node_key": "Pad001", "node_type": "pad", "validation_state": "failed", "properties_hash": "def..."}
],
"edges": [
{"source_key": "Sketch001", "target_key": "Pad001"}
]
}
```
3. Complete the job via `POST /api/runner/jobs/{jobID}/complete` with the summary result.
---
## 9. SSE Events
Clients should listen for these events on `GET /api/events`:
| Event | Payload | When |
|-------|---------|------|
| `dag.updated` | `{item_id, part_number, revision_number, node_count, edge_count}` | After any DAG sync |
| `dag.validated` | `{item_id, part_number, valid, failed_count}` | After validation completes |
| `job.created` | `{job_id, definition_name, trigger, item_id}` | Job auto-triggered or manually created |
| `job.claimed` | `{job_id, runner_id, runner}` | Runner claims a job |
| `job.progress` | `{job_id, progress, message}` | Runner reports progress |
| `job.completed` | `{job_id, runner_id}` | Job finishes successfully |
| `job.failed` | `{job_id, runner_id, error}` | Job fails |
| `job.cancelled` | `{job_id, cancelled_by}` | Job cancelled by user |
---
## 10. Cross-Item Edges
For assembly constraints that reference geometry in child parts (e.g. a mate constraint between two parts), use the `dag_cross_edges` table. These edges bridge the BOM DAG and the feature DAG.
Cross-item edges are **not** included in the standard `PUT /dag` sync. They will be managed through a dedicated endpoint in a future iteration once the assembly constraint model in Create/silo-mod is finalized.
For now, the DAG sync covers intra-item dependencies only. Assembly-level interference detection uses the BOM DAG (`relationships` table) combined with per-item feature DAGs.

View File

@@ -36,7 +36,7 @@ These files/directories exist only in Kindred Create and can be copied directly
| Directory | Description |
|-----------|-------------|
| `kindred-icons/` | 1444 Catppuccin Mocha SVG icon overrides |
| `icons/kindred/` | 1444 Catppuccin Mocha SVG icon overrides |
| `mods/silo/` | Silo workbench submodule (`silo-mod`) |
| `mods/ztools/` | ZTools workbench submodule |
| `src/Mod/Create/` | Kindred Create workbench (InitGui.py, kc_format.py, update_checker.py, version.py.in) |
@@ -74,46 +74,71 @@ These files/directories exist only in Kindred Create and can be copied directly
36 Kindred commits touch core FreeCAD C++ files. Of those, **38 files** also changed on `upstream/main`, creating potential conflicts. Listed in chronological order (oldest first) for cherry-pick sequence.
### Category legend
| Category | Meaning | Long-term plan |
|----------|---------|----------------|
| **1 — Extension** | Platform extension points for Python addons. Core differentiator. | KEEP — maintain and isolate |
| **2 — Branding** | Branding and theming. Unavoidable in a fork. | KEEP — minimize surface area |
| **3 — Bug fix** | Bug fixes and polish applicable to upstream FreeCAD. | UPSTREAM — contribute and eliminate |
### Commit sequence
| # | Hash | Summary | Conflict Risk | Files |
|---|------|---------|---------------|-------|
| 1 | `316d4f4b524` | Initial branding (CMakeLists, splash, about, theme, icons, QRC) | **HIGH** — CMakeLists.txt, DlgAbout.cpp, SplashScreen.cpp, resource.qrc all changed upstream | 14 files |
| 2 | `8c6837cc152` | Theme padding/clipping fixes | LOW — KindredCreate.qss is new file | 1 file |
| 3 | `bb3f3ac6d6c` | Dock task panel right, remove non-Kindred themes | **MEDIUM** — MainWindow.cpp, PreferencePacks CMakeLists changed upstream | 8 files |
| 4 | `e85162947b7` | Startup theme selector fix | **MEDIUM** — StartupProcess.cpp changed upstream | 4 files |
| 5 | `eb80c07f57a` | Theme QSS refinements | LOW — Kindred-only files | 4 files |
| 6 | `8639b6fd8ab` | Resolve unknown command/style token errors | **MEDIUM** — StartupProcess.cpp | 5 files |
| 7 | `fea1280fa90` | Theme alternate-background-color | LOW | 2 files |
| 8 | `b3fedfb19fb` | Theme tree item padding | LOW | 2 files |
| 9 | `0d4545b7d67` | Tree branch line SVGs | LOW — new files | 8 files |
| 10 | `434ae797a43` | Theme selector cleanup | **MEDIUM** — StartupProcess.cpp | 4 files |
| 11 | `224feda4ad6` | Catppuccin icon override infrastructure | **MEDIUM** — BitmapFactory.cpp, CMakeLists.txt | 2 files |
| 12 | `7535a48ec4c` | Origin abstraction layer | **HIGH** — Application.cpp, ApplicationPy.cpp/.h, CMakeLists.txt | 6 files (4 new) |
| 13 | `38358e431d2` | LocalFileOrigin and Std_* delegation | **HIGH** — CommandDoc.cpp, FileOrigin.cpp/.h | 3 files |
| 14 | `79c85ed2e5d` | FileOriginPython interactive methods | LOW — Kindred-only files | 3 files |
| 15 | `deeb6376f71` | OriginSelectorWidget | **HIGH** — Action.cpp/.h, CommandStd.cpp, Workbench.cpp, CMakeLists.txt | 8 files |
| 16 | `679aaec6d49` | Origin commands (Commit/Pull/Push) | **MEDIUM** — Application.cpp, Command.h, Workbench.cpp | 5 files |
| 17 | `db85277f262` | OriginManagerDialog | LOW — new files + OriginSelectorWidget.cpp | 3 files |
| 18 | `015df38328c` | Document-origin tracking in window title | **MEDIUM** — MDIView.cpp | 3 files |
| 19 | `a6e84552da5` | Cross-origin detection in SaveAs | **MEDIUM** — CommandDoc.cpp | 1 file |
| 20 | `84b69b935b2` | Build fix: remove SelectModule.h include | LOW | 1 file |
| 21 | `2f594dac0a5` | Use Python API for viewDefaultOrientation | **MEDIUM** — CommandDoc.cpp | 1 file |
| 22 | `724440dcb75` | Build fixes for OriginSelectorWidget/Manager | LOW — Kindred files | 2 files |
| 23 | `c858706d480` | Add silo icons to QRC | LOW — resource.qrc (additive) | 6 files |
| 24 | `d95c850b7b1` | Widen origin selector widget | LOW | 1 file |
| 25 | `10b5c9d584f` | Wire OriginManagerDialog to Silo_Settings | LOW | 1 file |
| 26 | `cc5ba638d1f` | UI polish — Wayland scaling, menu icon size | **HIGH** — DlgSettingsGeneral.cpp/.h/.ui changed upstream | 6 files |
| 27 | `4bf74cf3391` | Build fix for DlgSettingsGeneral | **MEDIUM** — DlgSettingsGeneral.h, StartupProcess.cpp | 2 files |
| 28 | `6773ca0dfd8` | Eliminate QSS/CFG duplication | LOW — Kindred files | 3 files |
| 29 | `977fa3c9347` | QGroupBox indicator, hyperlink color | LOW — KindredCreate.qss | 1 file |
| 30 | `8b2ce4b73a4` | Native Qt start panel + kindred:// URL | **MEDIUM** — MainWindow.cpp | 1 file |
| 31 | `bf637af4e45` | Window flickering and icon clipping fix | **MEDIUM** — MainWindow.cpp/.h | 3 files |
| 32 | `70118201b02` | MDI pre-document tab for Silo new item | **HIGH** — ApplicationPy, BreadcrumbToolBar, EditingContext, MainWindow, Workbench | 11 files |
| 33 | `1f49e3fa6da` | Build fix: ToolBarItem incomplete type, reportException | **LOW** — likely already fixed upstream | 2 files |
| 34 | `f71decca089` | Splash screen: skip runtime draw, mantle bg | **MEDIUM** — SplashScreen.cpp | 3 files |
| 35 | `2b0c6774c07` | Dev build defaults, skip version migration | **MEDIUM** — CMakeLists.txt, DlgVersionMigrator.cpp | 2 files |
| 36 | `ab71902a4c2` | Forward visibility arg in appendToolbar() | LOW — FreeCADGuiInit.py | 1 file |
| # | Hash | Summary | Category | Conflict Risk | Files |
|---|------|---------|----------|---------------|-------|
| 1 | `316d4f4b524` | Initial branding (CMakeLists, splash, about, theme, icons, QRC) | 2 — Branding | **HIGH** — CMakeLists.txt, DlgAbout.cpp, SplashScreen.cpp, resource.qrc all changed upstream | 14 files |
| 2 | `8c6837cc152` | Theme padding/clipping fixes | 2 — Branding | LOW — KindredCreate.qss is new file | 1 file |
| 3 | `bb3f3ac6d6c` | Dock task panel right, remove non-Kindred themes | 2 — Branding | **MEDIUM** — MainWindow.cpp, PreferencePacks CMakeLists changed upstream | 8 files |
| 4 | `e85162947b7` | Startup theme selector fix | 2 — Branding | **MEDIUM** — StartupProcess.cpp changed upstream | 4 files |
| 5 | `eb80c07f57a` | Theme QSS refinements | 2 — Branding | LOW — Kindred-only files | 4 files |
| 6 | `8639b6fd8ab` | Resolve unknown command/style token errors | 2 — Branding | **MEDIUM** — StartupProcess.cpp | 5 files |
| 7 | `fea1280fa90` | Theme alternate-background-color | 2 — Branding | LOW | 2 files |
| 8 | `b3fedfb19fb` | Theme tree item padding | 2 — Branding | LOW | 2 files |
| 9 | `0d4545b7d67` | Tree branch line SVGs | 2 — Branding | LOW — new files | 8 files |
| 10 | `434ae797a43` | Theme selector cleanup | 2 — Branding | **MEDIUM** — StartupProcess.cpp | 4 files |
| 11 | `224feda4ad6` | Catppuccin icon override infrastructure | 2 — Branding | **MEDIUM** — BitmapFactory.cpp, CMakeLists.txt | 2 files |
| 12 | `7535a48ec4c` | Origin abstraction layer | 1 — Extension | **HIGH** — Application.cpp, ApplicationPy.cpp/.h, CMakeLists.txt | 6 files (4 new) |
| 13 | `38358e431d2` | LocalFileOrigin and Std_* delegation | 1 — Extension | **HIGH** — CommandDoc.cpp, FileOrigin.cpp/.h | 3 files |
| 14 | `79c85ed2e5d` | FileOriginPython interactive methods | 1 — Extension | LOW — Kindred-only files | 3 files |
| 15 | `deeb6376f71` | OriginSelectorWidget | 1 — Extension | **HIGH** — Action.cpp/.h, CommandStd.cpp, Workbench.cpp, CMakeLists.txt | 8 files |
| 16 | `679aaec6d49` | Origin commands (Commit/Pull/Push) | 1 — Extension | **MEDIUM** — Application.cpp, Command.h, Workbench.cpp | 5 files |
| 17 | `db85277f262` | OriginManagerDialog | 1 — Extension | LOW — new files + OriginSelectorWidget.cpp | 3 files |
| 18 | `015df38328c` | Document-origin tracking in window title | 1 — Extension | **MEDIUM** — MDIView.cpp | 3 files |
| 19 | `a6e84552da5` | Cross-origin detection in SaveAs | 1 — Extension | **MEDIUM** — CommandDoc.cpp | 1 file |
| 20 | `84b69b935b2` | Build fix: remove SelectModule.h include | 1 — Extension | LOW | 1 file |
| 21 | `2f594dac0a5` | Use Python API for viewDefaultOrientation | 1 — Extension | **MEDIUM** — CommandDoc.cpp | 1 file |
| 22 | `724440dcb75` | Build fixes for OriginSelectorWidget/Manager | 1 — Extension | LOW — Kindred files | 2 files |
| 23 | `c858706d480` | Add silo icons to QRC | 2 — Branding | LOW — resource.qrc (additive) | 6 files |
| 24 | `d95c850b7b1` | Widen origin selector widget | 1 — Extension | LOW | 1 file |
| 25 | `10b5c9d584f` | Wire OriginManagerDialog to Silo_Settings | 1 — Extension | LOW | 1 file |
| 26 | `cc5ba638d1f` | UI polish — Wayland scaling, menu icon size | 3 — Bug fix | **HIGH** — DlgSettingsGeneral.cpp/.h/.ui changed upstream | 6 files |
| 27 | `4bf74cf3391` | Build fix for DlgSettingsGeneral | 3 — Bug fix | **MEDIUM** — DlgSettingsGeneral.h, StartupProcess.cpp | 2 files |
| 28 | `6773ca0dfd8` | Eliminate QSS/CFG duplication | 2 — Branding | LOW — Kindred files | 3 files |
| 29 | `977fa3c9347` | QGroupBox indicator, hyperlink color | 2 — Branding | LOW — KindredCreate.qss | 1 file |
| 30 | `8b2ce4b73a4` | Native Qt start panel + kindred:// URL | 1 — Extension | **MEDIUM** — MainWindow.cpp | 1 file |
| 31 | `bf637af4e45` | Window flickering and icon clipping fix | 3 — Bug fix | **MEDIUM** — MainWindow.cpp/.h | 3 files |
| 32 | `70118201b02` | MDI pre-document tab for Silo new item | 1 — Extension | **HIGH** — ApplicationPy, BreadcrumbToolBar, EditingContext, MainWindow, Workbench | 11 files |
| 33 | `1f49e3fa6da` | Build fix: ToolBarItem incomplete type, reportException | 3 — Bug fix | **LOW** — Kindred-specific build fix | 2 files |
| 34 | `f71decca089` | Splash screen: skip runtime draw, mantle bg | 2 — Branding | **MEDIUM** — SplashScreen.cpp | 3 files |
| 35 | `2b0c6774c07` | Dev build defaults, skip version migration | 2 — Branding | **MEDIUM** — CMakeLists.txt, DlgVersionMigrator.cpp | 2 files |
| 36 | `ab71902a4c2` | Forward visibility arg in appendToolbar() | 1 — Extension | LOW — FreeCADGuiInit.py | 1 file |
### Category summary
| Category | Count | Commits |
|----------|-------|---------|
| 1 — Extension | 16 | #1222, #2425, #30, #32, #36 |
| 2 — Branding | 16 | #111, #23, #2829, #3435 |
| 3 — Bug fix | 4 | #2627, #31, #33 |
### Category 3 — Upstream status (verified Feb 2026)
| # | Summary | Upstream Fixed? | FreeCAD Issues | Notes |
|---|---------|-----------------|----------------|-------|
| 26 | Wayland scaling, menu icon size pref | PARTIAL | [#25448](https://github.com/FreeCAD/FreeCAD/issues/25448), [#23830](https://github.com/FreeCAD/FreeCAD/issues/23830), [#23396](https://github.com/FreeCAD/FreeCAD/issues/23396) | Upstream has some HiDPI C++ fixes but Wayland fractional scaling still open. Menu icon size pref is a Kindred-added feature, not yet contributed. |
| 27 | Build fix for DlgSettingsGeneral | NO | — | Companion to #26. `setMenuIconSize()` in StartupProcess is Kindred-added. |
| 31 | Window flickering and icon clipping | NO | [#12917](https://github.com/FreeCAD/FreeCAD/issues/12917), [#18481](https://github.com/FreeCAD/FreeCAD/issues/18481) | Upstream still uses `setStyleSheet()` on statusBar causing repaint cascade. Good upstream PR candidate — replace with `setPalette()`. |
| 33 | ToolBarItem incomplete type, reportException | N/A | — | Kindred-internal build fix. The `ToolBarItem` forward-decl issue only manifests with Kindred modifications to Workbench.h. The `reportException` typo is in Kindred-only editing context code. |
### HIGH conflict files (need manual attention)
@@ -137,14 +162,14 @@ These files are modified by both Kindred and upstream. They will almost certainl
## Phase 3: Module-Level Changes
### Assembly fixes (check if still needed)
### Assembly fixes (verified Feb 2026)
| Hash | Summary | Status |
|------|---------|--------|
| `316d4f4b524` | Joint flip overconstrain fix | May be fixed upstream — verify |
| `9dc50cef727` | SIGSEGV during document restore | May be fixed upstream — verify |
| `ddefb236521` | Solver ignoring datum plane refs | May be fixed upstream — verify |
| `b7374d7b1fc` | findPlacement() datum/origin handling | May be fixed upstream — verify |
| Hash | Summary | Upstream Status | Action |
|------|---------|-----------------|--------|
| `316d4f4b524` | Joint flip overconstrain fix (91° rotation guard) | **NOT fixed** — upstream has basic grounded-object validation only. [FreeCAD#20377](https://github.com/FreeCAD/FreeCAD/issues/20377) open. | KEEP — re-apply |
| `9dc50cef727` | SIGSEGV during document restore (`isRestoring()` guard) | **NOT fixed** — upstream `onChanged()` has no restore guard. [FreeCAD#18225](https://github.com/FreeCAD/FreeCAD/issues/18225) closed via different path. | KEEP — re-apply |
| `ddefb236521` | Solver ignoring datum plane refs (`PartDesign::Plane`/`Point`) | **NOT fixed** — upstream `findPlacement()` lacks these types. | KEEP — re-apply, upstream candidate |
| `b7374d7b1fc` | findPlacement() datum/origin handling (`App::Plane`/`Point`) | **PARTIAL**`App::Line` fixed upstream via [PR#20026](https://github.com/FreeCAD/FreeCAD/pull/20026). `App::Plane`/`App::Point` still missing. | KEEP — re-apply the `Plane`/`Point` parts only |
Files touched: `src/Mod/Assembly/App/AssemblyObject.cpp`, `src/Mod/Assembly/UtilsAssembly.py`, `src/Mod/Assembly/InitGui.py`
@@ -179,7 +204,7 @@ Files touched: `src/Mod/Assembly/App/AssemblyObject.cpp`, `src/Mod/Assembly/Util
1. **Build**: Full CMake configure + build succeeds
2. **Launch**: Application starts without errors, splash and about dialogs show Kindred branding
3. **Theme**: KindredCreate theme loads correctly, QSS applies
4. **Icons**: kindred-icons override system works (BitmapFactory loads from kindred-icons/)
4. **Icons**: icon override system works (BitmapFactory loads from icons/kindred/)
5. **Origin system**: FileOrigin, OriginManager, OriginSelectorWidget functional
6. **Silo integration**: Auth panel, activity panel, save/commit/pull all work
7. **File format**: `.kc` files open/save with silo/ directory preserved
@@ -199,7 +224,7 @@ git fetch upstream
git checkout -b kindred-on-upstream-1.2 upstream/main
# 3. Copy Kindred-only directories
git checkout origin/main -- kindred-icons/ mods/ src/Mod/Create/ package/ docs/ \
git checkout origin/main -- icons/ mods/ src/Mod/Create/ package/ docs/ \
.gitea/ resources/kindred* banner-logo-light.png CONTRIBUTING.md .gitmodules
# 4. Copy new Kindred source files (no conflicts)

View File

@@ -19,7 +19,7 @@
- [Python as Source of Truth](./architecture/python-source-of-truth.md)
- [Silo Server](./architecture/silo-server.md)
- [Signal Architecture](./architecture/signal-architecture.md)
- [OndselSolver](./architecture/ondsel-solver.md)
- [KCSolve: Pluggable Solver](./architecture/ondsel-solver.md)
# Development
@@ -28,6 +28,7 @@
- [Repository Structure](./development/repo-structure.md)
- [Build System](./development/build-system.md)
- [Gui Module Build](./development/gui-build-integration.md)
- [Package.xml Schema Extensions](./development/package-xml-schema.md)
# Silo Server
@@ -45,6 +46,7 @@
- [Gap Analysis](./silo-server/GAP_ANALYSIS.md)
- [Frontend Spec](./silo-server/frontend-spec.md)
- [Installation](./silo-server/INSTALL.md)
- [Solver Service](./silo-server/SOLVER.md)
- [Roadmap](./silo-server/ROADMAP.md)
# Reference
@@ -63,3 +65,4 @@
- [OriginSelectorWidget](./reference/cpp-origin-selector-widget.md)
- [FileOriginPython Bridge](./reference/cpp-file-origin-python.md)
- [Creating a Custom Origin (C++)](./reference/cpp-custom-origin-guide.md)
- [KCSolve Python API](./reference/kcsolve-python.md)

View File

@@ -1,27 +1,132 @@
# OndselSolver
# KCSolve: Pluggable Solver Architecture
OndselSolver is the assembly constraint solver used by FreeCAD's Assembly workbench. Kindred Create vendors a fork of the solver as a git submodule.
KCSolve is the pluggable assembly constraint solver framework for Kindred Create. It defines an abstract solver interface (`IKCSolver`) and a runtime registry (`SolverRegistry`) that lets the Assembly module work with any conforming solver backend. The default backend wraps OndselSolver via `OndselAdapter`.
- **Path:** `src/3rdParty/OndselSolver/`
- **Source:** `git.kindred-systems.com/kindred/solver` (Kindred fork)
- **Library:** `src/Mod/Assembly/Solver/` (builds `libKCSolve.so`)
- **Python module:** `src/Mod/Assembly/Solver/bindings/` (builds `kcsolve.so`)
- **Tests:** `tests/src/Mod/Assembly/Solver/` (C++), `src/Mod/Assembly/AssemblyTests/TestKCSolvePy.py` (Python)
## How it works
## Architecture
The solver uses a **Lagrangian constraint formulation** to resolve assembly constraints (mates, joints, fixed positions). Given a set of parts with geometric constraints between them, it computes positions and orientations that satisfy all constraints simultaneously.
```
┌──────────────────────────────────────────────────┐
│ Assembly Module (AssemblyObject.cpp) │
│ Builds SolveContext from FreeCAD document, │
│ calls solver via SolverRegistry │
├──────────────────────────────────────────────────┤
│ SolverRegistry (singleton) │
│ register_solver(), get(), available() │
│ Plugin discovery via scan() / scan_default_paths │
├──────────────┬───────────────────────────────────┤
│ OndselAdapter │ Python solvers │ Future plugins │
│ (C++ built-in)│ (via kcsolve) │ (.so plugins) │
└──────────────┴───────────────────────────────────┘
```
The Assembly workbench (`src/Mod/Assembly/`) calls the solver whenever constraints are added or modified. Kindred Create has patches to `Assembly/` that extend `findPlacement()` for better datum and origin handling.
The Assembly module never references OndselSolver directly. All solver access goes through `SolverRegistry::instance().get()`, which returns a `std::unique_ptr<IKCSolver>`.
## Why a fork
## IKCSolver interface
The solver is forked from the upstream Ondsel project for:
- **Pinned stability** — the submodule is pinned to a known-good commit
- **Potential modifications** — the fork allows Kindred-specific patches if needed
- **Availability** — hosted on Kindred's Gitea instance for reliable access
A solver backend implements `IKCSolver` (defined in `IKCSolver.h`). Only three methods are pure virtual; all others have sensible defaults:
## Future: GNN solver
| Method | Required | Purpose |
|--------|----------|---------|
| `name()` | yes | Human-readable solver name |
| `supported_joints()` | yes | List of `BaseJointKind` values the solver handles |
| `solve(ctx)` | yes | Solve for static equilibrium |
| `update(ctx)` | no | Incremental re-solve after parameter changes |
| `pre_drag(ctx, parts)` | no | Begin interactive drag session |
| `drag_step(placements)` | no | One mouse-move during drag |
| `post_drag()` | no | End drag session |
| `run_kinematic(ctx)` | no | Run kinematic simulation |
| `num_frames()` | no | Frame count after simulation |
| `update_for_frame(i)` | no | Retrieve frame placements |
| `diagnose(ctx)` | no | Detect redundant/conflicting constraints |
| `is_deterministic()` | no | Whether output is reproducible (default: true) |
| `export_native(path)` | no | Write solver-native debug file (e.g. ASMT) |
| `supports_bundle_fixed()` | no | Whether solver handles Fixed-joint bundling internally |
There are plans to explore a Graph Neural Network (GNN) approach to constraint solving that could complement or supplement the Lagrangian solver for specific use cases. This is not yet implemented.
## Core types
## Related: GSL
All types live in `Types.h` with no FreeCAD dependencies, making the header standalone for future server/worker use.
The `src/3rdParty/GSL/` submodule is Microsoft's Guidelines Support Library (`github.com/microsoft/GSL`), providing C++ core guidelines utilities like `gsl::span` and `gsl::not_null`. It is a build dependency, not related to the constraint solver.
**Transform** -- position `[x, y, z]` + unit quaternion `[w, x, y, z]`. Equivalent to `Base::Placement` but independent. Note the quaternion convention differs from `Base::Rotation` which uses `(x, y, z, w)` ordering; the adapter layer handles the swap.
**BaseJointKind** -- 24 primitive constraint types decomposed from FreeCAD's `JointType` and `DistanceType` enums. Covers point constraints (Coincident, PointOnLine, PointInPlane), axis/surface constraints (Concentric, Tangent, Planar), kinematic joints (Fixed, Revolute, Cylindrical, Slider, Ball, Screw, Universal), mechanical elements (Gear, RackPinion), distance variants, and a `Custom` extension point.
**SolveContext** -- complete solver input: parts (with placements, mass, grounded flag), constraints (with markers, parameters, limits), optional motion definitions and simulation parameters.
**SolveResult** -- solver output: status code, updated part placements, DOF count, constraint diagnostics, and simulation frame count.
## SolverRegistry
Thread-safe singleton managing solver backends:
```cpp
auto& reg = SolverRegistry::instance();
// Registration (at module init)
reg.register_solver("ondsel", []() {
return std::make_unique<OndselAdapter>();
});
// Retrieval
auto solver = reg.get(); // default solver
auto solver = reg.get("ondsel"); // by name
// Queries
reg.available(); // ["ondsel", ...]
reg.joints_for("ondsel"); // [Fixed, Revolute, ...]
reg.set_default("ondsel");
```
Plugin discovery scans directories for shared libraries exporting `kcsolve_api_version()` and `kcsolve_create()`. Default paths: `KCSOLVE_PLUGIN_PATH` env var and `<prefix>/lib/kcsolve/`.
## OndselAdapter
The built-in solver backend wrapping OndselSolver's Lagrangian constraint formulation. Registered as `"ondsel"` at Assembly module initialization.
Supports all 24 joint types. The adapter translates between `SolveContext`/`SolveResult` and OndselSolver's internal `ASMTAssembly` representation, including:
- Part placement conversion (Transform <-> Base::Placement quaternion ordering)
- Constraint parameter mapping (BaseJointKind -> OndselSolver joint classes)
- Interactive drag protocol (pre_drag/drag_step/post_drag)
- Kinematic simulation (run_kinematic/num_frames/update_for_frame)
- Constraint diagnostics (redundancy detection via MbD system)
## Python bindings (kcsolve module)
The `kcsolve` pybind11 module exposes the full C++ API to Python. See [KCSolve Python API](../reference/kcsolve-python.md) for details.
Key capabilities:
- All enums, structs, and classes accessible from Python
- Subclass `IKCSolver` in pure Python to create new solver backends
- Register Python solvers at runtime via `kcsolve.register_solver()`
- Query the registry from the FreeCAD console
## File layout
```
src/Mod/Assembly/Solver/
├── Types.h # Enums and structs (no FreeCAD deps)
├── IKCSolver.h # Abstract solver interface
├── SolverRegistry.h/cpp # Singleton registry + plugin loading
├── OndselAdapter.h/cpp # OndselSolver wrapper
├── KCSolveGlobal.h # DLL export macros
├── CMakeLists.txt # Builds libKCSolve.so
└── bindings/
├── PyIKCSolver.h # pybind11 trampoline for Python subclasses
├── kcsolve_py.cpp # Module definition (enums, structs, classes)
└── CMakeLists.txt # Builds kcsolve.so (pybind11 module)
```
## Testing
- **18 C++ tests** (`KCSolve_tests_run`) covering SolverRegistry (8 tests) and OndselAdapter (10 tests including drag protocol and redundancy diagnosis)
- **16 Python tests** (`TestKCSolvePy`) covering module import, type bindings, registry functions, Python solver subclassing, and full register/load/solve round-trips
- **6 Python integration tests** (`TestSolverIntegration`) testing solver behavior through FreeCAD document objects
## Related
- [KCSolve Python API Reference](../reference/kcsolve-python.md)
- [INTER_SOLVER.md](../../INTER_SOLVER.md) -- full architecture specification

View File

@@ -0,0 +1,109 @@
# Package.xml Schema Extensions
Kindred Create extends FreeCAD's standard `package.xml` addon manifest with a `<kindred>` element. This element provides metadata for the manifest-driven addon loader: version compatibility, load ordering, dependency declarations, and editing context registration.
The `<kindred>` element is ignored by FreeCAD's AddonManager and stock module loader. Addons with this element remain compatible with upstream FreeCAD.
## Field reference
| Field | Parsed by loader | Required | Default | Description |
|---|---|---|---|---|
| `min_create_version` | Yes | No | *(none)* | Minimum Kindred Create version. Addon is skipped if the running version is lower. |
| `max_create_version` | Yes | No | *(none)* | Maximum Kindred Create version. Addon is skipped if the running version is higher. |
| `load_priority` | Yes | No | `100` | Integer. Lower values load first. Used as a secondary sort after dependency resolution. |
| `dependencies` | Yes | No | *(none)* | List of addon names (by `<name>` in their `package.xml`) that must load before this one. |
| `sdk_version` | No | No | *(none)* | Required kindred-addon-sdk version. Reserved for future use when the SDK is available. |
| `pure_python` | No | No | `true` | If `false`, the addon requires compiled C++ components. Informational. |
| `contexts` | No | No | *(none)* | Editing contexts this addon registers or injects into. Informational. |
Fields marked "Parsed by loader" are read by `addon_loader.py` and affect load behavior. Other fields are informational metadata for tooling and documentation.
## Schema
```xml
<kindred>
<min_create_version>0.1.0</min_create_version>
<max_create_version>1.0.0</max_create_version>
<sdk_version>0.1.0</sdk_version>
<load_priority>100</load_priority>
<pure_python>true</pure_python>
<dependencies>
<dependency>sdk</dependency>
<dependency>other-addon</dependency>
</dependencies>
<contexts>
<context id="partdesign.body" action="inject"/>
<context id="sketcher.edit" action="register"/>
<context id="*" action="overlay"/>
</contexts>
</kindred>
```
All child elements are optional. An empty `<kindred/>` element is valid and signals that the addon is Kindred-aware with all defaults.
### Version fields
`min_create_version` and `max_create_version` are compared against the running Kindred Create version using semantic versioning (major.minor.patch). If the running version falls outside the declared range, the addon is skipped with a warning in the report view.
### Load priority
When multiple addons have no dependency relationship, `load_priority` determines their relative order. Lower values load first. Suggested ranges:
| Range | Use |
|---|---|
| 0-9 | SDK and core infrastructure |
| 10-49 | Foundation addons that others may depend on |
| 50-99 | Standard addons |
| 100+ | Optional or late-loading addons |
### Dependencies
Each `<dependency>` names another addon by its `<name>` element in `package.xml`. The loader resolves load order using topological sort. If a dependency is not found among discovered addons, the dependent addon is skipped.
### Contexts
The `<contexts>` element documents which editing contexts the addon interacts with. The `action` attribute describes the type of interaction:
| Action | Meaning |
|---|---|
| `inject` | Addon injects commands into this context's toolbars |
| `register` | Addon registers this as a new context |
| `overlay` | Addon registers an overlay that may apply across contexts |
A wildcard `id="*"` indicates the addon's overlay applies universally (matched by a condition function rather than a specific context ID).
## Example: complete package.xml
```xml
<?xml version="1.0" encoding="UTF-8"?>
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>MyAddon</name>
<description>Example Kindred Create addon</description>
<version>0.2.0</version>
<maintainer email="dev@example.com">Developer</maintainer>
<license>LGPL-2.1-or-later</license>
<url type="repository">https://git.example.com/myaddon</url>
<content>
<workbench>
<classname>MyAddonWorkbench</classname>
<subdirectory>./</subdirectory>
</workbench>
</content>
<kindred>
<min_create_version>0.1.0</min_create_version>
<load_priority>80</load_priority>
<pure_python>true</pure_python>
<contexts>
<context id="partdesign.body" action="inject"/>
</contexts>
</kindred>
</package>
```
## Backward compatibility
- The `<kindred>` element sits outside `<content>` and is not part of FreeCAD's package.xml specification. FreeCAD's `App.Metadata` C++ parser and the AddonManager's Python `MetadataReader` both ignore unknown elements.
- An addon installed in stock FreeCAD will work normally; the `<kindred>` extensions are simply unused.
- The Kindred Create loader works with or without the `<kindred>` element. Addons that omit it load with no version constraints and default priority (100).

View File

@@ -20,7 +20,10 @@ create/
├── mods/ # Kindred addon workbenches (submodules)
│ ├── ztools/ # ztools workbench
│ └── silo/ # Silo parts database
├── kindred-icons/ # SVG icon library (~200 icons)
├── icons/ # Icon theming (kindred overrides, palettes, retheme script)
│ ├── kindred/ # Hand-crafted Catppuccin Mocha SVG overrides (~1444 icons)
│ ├── mappings/ # Color palette CSVs (FCAD.csv, kindred.csv)
│ └── retheme.py # Script to remap upstream icon colors
├── resources/ # Branding, desktop integration
│ ├── branding/ # Logo, splash, icon generation scripts
│ └── icons/ # Platform icons (.ico, .icns, hicolor)

View File

@@ -0,0 +1,429 @@
# KCSolve Python API Reference
The `kcsolve` module provides Python access to the KCSolve pluggable solver framework. It is built with pybind11 and installed alongside the Assembly module.
```python
import kcsolve
```
## Module constants
| Name | Value | Description |
|------|-------|-------------|
| `API_VERSION_MAJOR` | `1` | KCSolve API major version |
## Enums
### BaseJointKind
Primitive constraint types. 24 values:
`Coincident`, `PointOnLine`, `PointInPlane`, `Concentric`, `Tangent`, `Planar`, `LineInPlane`, `Parallel`, `Perpendicular`, `Angle`, `Fixed`, `Revolute`, `Cylindrical`, `Slider`, `Ball`, `Screw`, `Universal`, `Gear`, `RackPinion`, `Cam`, `Slot`, `DistancePointPoint`, `DistanceCylSph`, `Custom`
### SolveStatus
| Value | Meaning |
|-------|---------|
| `Success` | Solve converged |
| `Failed` | Solve did not converge |
| `InvalidFlip` | Orientation flipped past threshold |
| `NoGroundedParts` | No grounded parts in assembly |
### DiagnosticKind
`Redundant`, `Conflicting`, `PartiallyRedundant`, `Malformed`
### MotionKind
`Rotational`, `Translational`, `General`
### LimitKind
`TranslationMin`, `TranslationMax`, `RotationMin`, `RotationMax`
## Structs
### Transform
Rigid-body transform: position + unit quaternion.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `position` | `list[float]` (3) | `[0, 0, 0]` | Translation (x, y, z) |
| `quaternion` | `list[float]` (4) | `[1, 0, 0, 0]` | Unit quaternion (w, x, y, z) |
```python
t = kcsolve.Transform()
t = kcsolve.Transform.identity() # same as default
```
Note: quaternion convention is `(w, x, y, z)`, which differs from FreeCAD's `Base.Rotation(x, y, z, w)`. The adapter layer handles conversion.
### Part
| Field | Type | Default |
|-------|------|---------|
| `id` | `str` | `""` |
| `placement` | `Transform` | identity |
| `mass` | `float` | `1.0` |
| `grounded` | `bool` | `False` |
### Constraint
A constraint between two parts, built from a FreeCAD JointObject by the adapter layer.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `id` | `str` | `""` | FreeCAD document object name (e.g. `"Joint001"`) |
| `part_i` | `str` | `""` | Solver-side part ID for first reference |
| `marker_i` | `Transform` | identity | Coordinate system on `part_i` (attachment point/orientation) |
| `part_j` | `str` | `""` | Solver-side part ID for second reference |
| `marker_j` | `Transform` | identity | Coordinate system on `part_j` (attachment point/orientation) |
| `type` | `BaseJointKind` | `Coincident` | Constraint type |
| `params` | `list[float]` | `[]` | Scalar parameters (interpretation depends on `type`) |
| `limits` | `list[Constraint.Limit]` | `[]` | Joint travel limits |
| `activated` | `bool` | `True` | Whether this constraint is active |
**`marker_i` / `marker_j`** -- Define the local coordinate frames on each part where the joint acts. For example, a Revolute joint's markers define the hinge axis direction and attachment points on each part.
**`params`** -- Interpretation depends on `type`:
| Type | params[0] | params[1] |
|------|-----------|-----------|
| `Angle` | angle (radians) | |
| `RackPinion` | pitch radius | |
| `Screw` | pitch | |
| `Gear` | radius I | radius J (negative for belt) |
| `DistancePointPoint` | distance | |
| `DistanceCylSph` | distance | |
| `Planar` | offset | |
| `Concentric` | distance | |
| `PointInPlane` | offset | |
| `LineInPlane` | offset | |
### Constraint.Limit
Joint travel limits (translation or rotation bounds).
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `kind` | `LimitKind` | `TranslationMin` | Which degree of freedom to limit |
| `value` | `float` | `0.0` | Limit value (meters for translation, radians for rotation) |
| `tolerance` | `float` | `1e-9` | Solver tolerance for limit enforcement |
### MotionDef
A motion driver for kinematic simulation. Defines time-dependent actuation of a constraint.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `kind` | `MotionKind` | `Rotational` | Type of motion: `Rotational`, `Translational`, or `General` (both) |
| `joint_id` | `str` | `""` | ID of the constraint this motion drives |
| `marker_i` | `str` | `""` | Reference marker on first part |
| `marker_j` | `str` | `""` | Reference marker on second part |
| `rotation_expr` | `str` | `""` | Rotation law as a function of time `t` (e.g. `"2*pi*t"`) |
| `translation_expr` | `str` | `""` | Translation law as a function of time `t` (e.g. `"10*t"`) |
For `Rotational` kind, only `rotation_expr` is used. For `Translational`, only `translation_expr`. For `General`, both are set.
### SimulationParams
Time-stepping parameters for kinematic simulation via `run_kinematic()`.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `t_start` | `float` | `0.0` | Simulation start time (seconds) |
| `t_end` | `float` | `1.0` | Simulation end time (seconds) |
| `h_out` | `float` | `0.01` | Output time step -- controls frame rate (e.g. `0.04` = 25 fps) |
| `h_min` | `float` | `1e-9` | Minimum internal integration step |
| `h_max` | `float` | `1.0` | Maximum internal integration step |
| `error_tol` | `float` | `1e-6` | Error tolerance for adaptive time stepping |
### SolveContext
Complete input to a solve operation. Built by the adapter layer from FreeCAD document objects, or constructed manually for scripted solving.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `parts` | `list[Part]` | `[]` | All parts in the assembly |
| `constraints` | `list[Constraint]` | `[]` | Constraints between parts |
| `motions` | `list[MotionDef]` | `[]` | Motion drivers for kinematic simulation |
| `simulation` | `SimulationParams` or `None` | `None` | Time-stepping parameters for `run_kinematic()` |
| `bundle_fixed` | `bool` | `False` | Hint to merge Fixed-joint-connected parts into rigid bodies |
**`motions`** -- Motion drivers define time-dependent joint actuation for kinematic simulation. Each `MotionDef` references a constraint by `joint_id` and provides expressions (functions of time `t`) for rotation and/or translation. Only used when calling `run_kinematic()`.
**`simulation`** -- When set, provides time-stepping parameters (`t_start`, `t_end`, step sizes, error tolerance) for kinematic simulation via `run_kinematic()`. When `None`, kinematic simulation is not requested.
**`bundle_fixed`** -- When `True`, parts connected by `Fixed` joints should be merged into single rigid bodies before solving, reducing the problem size. If the solver reports `supports_bundle_fixed() == True`, it handles this internally. Otherwise, the caller (adapter layer) pre-bundles before building the context.
**Important:** pybind11 returns copies of `list` fields, not references. Use whole-list assignment:
```python
ctx = kcsolve.SolveContext()
p = kcsolve.Part()
p.id = "box1"
ctx.parts = [p] # correct
# ctx.parts.append(p) # does NOT modify ctx
```
### ConstraintDiagnostic
| Field | Type | Default |
|-------|------|---------|
| `constraint_id` | `str` | `""` |
| `kind` | `DiagnosticKind` | `Redundant` |
| `detail` | `str` | `""` |
### SolveResult
| Field | Type | Default |
|-------|------|---------|
| `status` | `SolveStatus` | `Success` |
| `placements` | `list[SolveResult.PartResult]` | `[]` |
| `dof` | `int` | `-1` |
| `diagnostics` | `list[ConstraintDiagnostic]` | `[]` |
| `num_frames` | `int` | `0` |
### SolveResult.PartResult
| Field | Type | Default |
|-------|------|---------|
| `id` | `str` | `""` |
| `placement` | `Transform` | identity |
## Classes
### IKCSolver
Abstract base class for solver backends. Subclass in Python to create custom solvers.
Three methods must be implemented:
```python
class MySolver(kcsolve.IKCSolver):
def name(self):
return "My Solver"
def supported_joints(self):
return [kcsolve.BaseJointKind.Fixed, kcsolve.BaseJointKind.Revolute]
def solve(self, ctx):
result = kcsolve.SolveResult()
result.status = kcsolve.SolveStatus.Success
return result
```
All other methods are optional and have default implementations. Override them to add capabilities beyond basic solving.
#### update(ctx) -> SolveResult
Incrementally re-solve after parameter changes (e.g. joint angle adjusted during creation). Solvers can optimize this path since only parameters changed, not topology. Default: delegates to `solve()`.
```python
def update(self, ctx):
# Only re-evaluate changed constraints, reuse cached factorization
return self._incremental_solve(ctx)
```
#### Interactive drag protocol
Three-phase protocol for interactive part dragging in the viewport. Solvers can maintain internal state across the drag session for better performance.
**pre_drag(ctx, drag_parts) -> SolveResult** -- Prepare for a drag session. `drag_parts` is a `list[str]` of part IDs being dragged. Solve the initial state and cache internal data. Default: delegates to `solve()`.
**drag_step(drag_placements) -> SolveResult** -- Called on each mouse move. `drag_placements` is a `list[SolveResult.PartResult]` with the current positions of dragged parts. Returns updated placements for all affected parts. Default: returns Success with no placements.
**post_drag()** -- End the drag session and release internal state. Default: no-op.
```python
def pre_drag(self, ctx, drag_parts):
self._cached_system = self._build_system(ctx)
return self.solve(ctx)
def drag_step(self, drag_placements):
# Use cached system for fast incremental solve
for dp in drag_placements:
self._cached_system.set_placement(dp.id, dp.placement)
return self._cached_system.solve_incremental()
def post_drag(self):
self._cached_system = None
```
#### Kinematic simulation
**run_kinematic(ctx) -> SolveResult** -- Run a kinematic simulation over the time range in `ctx.simulation`. After this call, `num_frames()` returns the frame count and `update_for_frame(i)` retrieves individual frames. Requires `ctx.simulation` to be set and `ctx.motions` to contain at least one motion driver. Default: returns Failed.
**num_frames() -> int** -- Number of simulation frames available after `run_kinematic()`. Default: returns 0.
**update_for_frame(index) -> SolveResult** -- Retrieve part placements for simulation frame at `index` (0-based, must be < `num_frames()`). Default: returns Failed.
```python
# Run a kinematic simulation
ctx.simulation = kcsolve.SimulationParams()
ctx.simulation.t_start = 0.0
ctx.simulation.t_end = 2.0
ctx.simulation.h_out = 0.04 # 25 fps
motion = kcsolve.MotionDef()
motion.kind = kcsolve.MotionKind.Rotational
motion.joint_id = "Joint001"
motion.rotation_expr = "2*pi*t" # one revolution per second
ctx.motions = [motion]
solver = kcsolve.load("ondsel")
result = solver.run_kinematic(ctx)
for i in range(solver.num_frames()):
frame = solver.update_for_frame(i)
for pr in frame.placements:
print(f"frame {i}: {pr.id} at {list(pr.placement.position)}")
```
#### diagnose(ctx) -> list[ConstraintDiagnostic]
Analyze the assembly for redundant, conflicting, or malformed constraints. May require a prior `solve()` call for some solvers. Returns a list of `ConstraintDiagnostic` objects. Default: returns empty list.
```python
diags = solver.diagnose(ctx)
for d in diags:
if d.kind == kcsolve.DiagnosticKind.Redundant:
print(f"Redundant: {d.constraint_id} - {d.detail}")
elif d.kind == kcsolve.DiagnosticKind.Conflicting:
print(f"Conflict: {d.constraint_id} - {d.detail}")
```
#### is_deterministic() -> bool
Whether this solver produces identical results given identical input. Used for regression testing and result caching. Default: returns `True`.
#### export_native(path)
Write a solver-native debug/diagnostic file (e.g. ASMT format for OndselSolver). Requires a prior `solve()` or `run_kinematic()` call. Default: no-op.
```python
solver.solve(ctx)
solver.export_native("/tmp/debug.asmt")
```
#### supports_bundle_fixed() -> bool
Whether this solver handles Fixed-joint part bundling internally. When `False`, the caller merges Fixed-joint-connected parts into single rigid bodies before building the `SolveContext`, reducing problem size. When `True`, the solver receives unbundled parts and optimizes internally. Default: returns `False`.
### OndselAdapter
Built-in solver wrapping OndselSolver's Lagrangian constraint formulation. Inherits `IKCSolver`.
```python
solver = kcsolve.OndselAdapter()
solver.name() # "OndselSolver (Lagrangian)"
```
In practice, use `kcsolve.load("ondsel")` rather than constructing directly, as this goes through the registry.
## Module functions
### available()
Return names of all registered solvers.
```python
kcsolve.available() # ["ondsel"]
```
### load(name="")
Create an instance of the named solver. If `name` is empty, uses the default. Returns `None` if the solver is not found.
```python
solver = kcsolve.load("ondsel")
solver = kcsolve.load() # default solver
```
### joints_for(name)
Query supported joint types for a registered solver.
```python
joints = kcsolve.joints_for("ondsel")
# [BaseJointKind.Coincident, BaseJointKind.Fixed, ...]
```
### set_default(name)
Set the default solver name. Returns `True` if the name is registered.
```python
kcsolve.set_default("ondsel") # True
kcsolve.set_default("unknown") # False
```
### get_default()
Get the current default solver name.
```python
kcsolve.get_default() # "ondsel"
```
### register_solver(name, solver_class)
Register a Python solver class with the SolverRegistry. `solver_class` must be a callable that returns an `IKCSolver` subclass instance.
```python
class MySolver(kcsolve.IKCSolver):
def name(self): return "MySolver"
def supported_joints(self): return [kcsolve.BaseJointKind.Fixed]
def solve(self, ctx):
r = kcsolve.SolveResult()
r.status = kcsolve.SolveStatus.Success
return r
kcsolve.register_solver("my_solver", MySolver)
solver = kcsolve.load("my_solver")
```
## Complete example
```python
import kcsolve
# Build a two-part assembly with a Fixed joint
ctx = kcsolve.SolveContext()
base = kcsolve.Part()
base.id = "base"
base.grounded = True
arm = kcsolve.Part()
arm.id = "arm"
arm.placement.position = [100.0, 0.0, 0.0]
joint = kcsolve.Constraint()
joint.id = "Joint001"
joint.part_i = "base"
joint.part_j = "arm"
joint.type = kcsolve.BaseJointKind.Fixed
ctx.parts = [base, arm]
ctx.constraints = [joint]
# Solve
solver = kcsolve.load("ondsel")
result = solver.solve(ctx)
print(result.status) # SolveStatus.Success
for pr in result.placements:
print(f"{pr.id}: pos={list(pr.placement.position)}")
# Diagnostics
diags = solver.diagnose(ctx)
for d in diags:
print(f"{d.constraint_id}: {d.kind} - {d.detail}")
```
## Related
- [KCSolve Architecture](../architecture/ondsel-solver.md)
- [INTER_SOLVER.md](../../INTER_SOLVER.md) -- full architecture specification

View File

@@ -73,25 +73,27 @@ database:
---
## Storage (MinIO/S3)
## Storage (Filesystem)
| Key | Type | Default | Env Override | Description |
|-----|------|---------|-------------|-------------|
| `storage.endpoint` | string | — | `SILO_MINIO_ENDPOINT` | MinIO/S3 endpoint (`host:port`) |
| `storage.access_key` | string | — | `SILO_MINIO_ACCESS_KEY` | Access key |
| `storage.secret_key` | string | — | `SILO_MINIO_SECRET_KEY` | Secret key |
| `storage.bucket` | string | — | — | S3 bucket name (created automatically if missing) |
| `storage.use_ssl` | bool | `false` | — | Use HTTPS for MinIO connections |
| `storage.region` | string | `"us-east-1"` | — | S3 region |
Files are stored on the local filesystem under a configurable root directory.
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `storage.backend` | string | `"filesystem"` | Storage backend (`filesystem`) |
| `storage.filesystem.root_dir` | string | — | Root directory for file storage (required) |
```yaml
storage:
endpoint: "localhost:9000"
access_key: "" # use SILO_MINIO_ACCESS_KEY env var
secret_key: "" # use SILO_MINIO_SECRET_KEY env var
bucket: "silo-files"
use_ssl: false
region: "us-east-1"
backend: "filesystem"
filesystem:
root_dir: "/opt/silo/data"
```
Ensure the directory exists and is writable by the `silo` user:
```bash
sudo mkdir -p /opt/silo/data
sudo chown silo:silo /opt/silo/data
```
---
@@ -264,9 +266,6 @@ All environment variable overrides. These take precedence over values in `config
| `SILO_DB_NAME` | `database.name` | PostgreSQL database name |
| `SILO_DB_USER` | `database.user` | PostgreSQL user |
| `SILO_DB_PASSWORD` | `database.password` | PostgreSQL password |
| `SILO_MINIO_ENDPOINT` | `storage.endpoint` | MinIO endpoint |
| `SILO_MINIO_ACCESS_KEY` | `storage.access_key` | MinIO access key |
| `SILO_MINIO_SECRET_KEY` | `storage.secret_key` | MinIO secret key |
| `SILO_SESSION_SECRET` | `auth.session_secret` | Session cookie signing secret |
| `SILO_ADMIN_USERNAME` | `auth.local.default_admin_username` | Default admin username |
| `SILO_ADMIN_PASSWORD` | `auth.local.default_admin_password` | Default admin password |
@@ -296,11 +295,9 @@ database:
sslmode: "disable"
storage:
endpoint: "localhost:9000"
access_key: "minioadmin"
secret_key: "minioadmin"
bucket: "silo-files"
use_ssl: false
backend: "filesystem"
filesystem:
root_dir: "./data"
schemas:
directory: "./schemas"

View File

@@ -4,7 +4,7 @@
> instructions. This document covers ongoing maintenance and operations for an
> existing deployment.
This guide covers deploying Silo to a dedicated VM using external PostgreSQL and MinIO services.
This guide covers deploying Silo to a dedicated VM using external PostgreSQL and local filesystem storage.
## Table of Contents
@@ -26,28 +26,25 @@ This guide covers deploying Silo to a dedicated VM using external PostgreSQL and
│ │ silod │ │
│ │ (Silo API Server) │ │
│ │ :8080 │ │
│ │ Files: /opt/silo/data │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────┐ ┌─────────────────────────────────┐
│ psql.example.internal │ │ minio.example.internal │
│ PostgreSQL 16 │ │ MinIO S3 │
│ :5432 │ │ :9000 (API) │
│ │ │ :9001 (Console) │
└─────────────────────────┘ └─────────────────────────────────┘
┌─────────────────────────┐
│ psql.example.internal │
│ PostgreSQL 16 │
│ :5432 │
└─────────────────────────┘
```
## External Services
The following external services are already configured:
| Service | Host | Database/Bucket | User |
|---------|------|-----------------|------|
| Service | Host | Database | User |
|---------|------|----------|------|
| PostgreSQL | psql.example.internal:5432 | silo | silo |
| MinIO | minio.example.internal:9000 | silo-files | silouser |
Migrations have been applied to the database.
Files are stored on the local filesystem at `/opt/silo/data`. Migrations have been applied to the database.
---
@@ -107,21 +104,15 @@ Fill in the values:
# Database credentials (psql.example.internal)
SILO_DB_PASSWORD=your-database-password
# MinIO credentials (minio.example.internal)
SILO_MINIO_ACCESS_KEY=silouser
SILO_MINIO_SECRET_KEY=your-minio-secret-key
```
### Verify External Services
Before deploying, verify connectivity to external services:
Before deploying, verify connectivity to PostgreSQL:
```bash
# Test PostgreSQL
psql -h psql.example.internal -U silo -d silo -c 'SELECT 1'
# Test MinIO
curl -I http://minio.example.internal:9000/minio/health/live
```
---
@@ -183,6 +174,7 @@ sudo -E /opt/silo/src/scripts/deploy.sh
| File | Purpose |
|------|---------|
| `/opt/silo/bin/silod` | Server binary |
| `/opt/silo/data/` | File storage root |
| `/opt/silo/src/` | Git repository checkout |
| `/etc/silo/config.yaml` | Server configuration |
| `/etc/silo/silod.env` | Environment variables (secrets) |
@@ -242,7 +234,7 @@ sudo journalctl -u silod --since "2024-01-15 10:00:00"
# Basic health check
curl http://localhost:8080/health
# Full readiness check (includes DB and MinIO)
# Full readiness check (includes DB)
curl http://localhost:8080/ready
```
@@ -318,24 +310,6 @@ psql -h psql.example.internal -U silo -d silo -f /opt/silo/src/migrations/008_ne
3. Check `pg_hba.conf` on PostgreSQL server allows connections from this host.
### Connection Refused to MinIO
1. Test network connectivity:
```bash
nc -zv minio.example.internal 9000
```
2. Test with curl:
```bash
curl -I http://minio.example.internal:9000/minio/health/live
```
3. Check SSL settings in config match MinIO setup:
```yaml
storage:
use_ssl: true # or false
```
### Health Check Fails
```bash
@@ -345,7 +319,9 @@ curl -v http://localhost:8080/ready
# If ready fails but health passes, check external services
psql -h psql.example.internal -U silo -d silo -c 'SELECT 1'
curl http://minio.example.internal:9000/minio/health/live
# Check file storage directory
ls -la /opt/silo/data
```
### Build Fails
@@ -460,10 +436,9 @@ sudo systemctl reload nginx
- [ ] `/etc/silo/silod.env` has mode 600 (`chmod 600`)
- [ ] Database password is strong and unique
- [ ] MinIO credentials are specific to silo (not admin)
- [ ] SSL/TLS enabled for PostgreSQL (`sslmode: require`)
- [ ] SSL/TLS enabled for MinIO (`use_ssl: true`) if available
- [ ] HTTPS enabled via nginx reverse proxy
- [ ] File storage directory (`/opt/silo/data`) owned by `silo` user with mode 750
- [ ] Silod listens on localhost only (`host: 127.0.0.1`)
- [ ] Firewall allows only ports 80, 443 (not 8080)
- [ ] Service runs as non-root `silo` user

View File

@@ -76,7 +76,7 @@ See [ROADMAP.md](ROADMAP.md) for the platform roadmap and dependency tier struct
| Append-only revision history | Complete | `internal/db/items.go` |
| Sequential revision numbering | Complete | Database trigger |
| Property snapshots (JSONB) | Complete | `revisions.properties` |
| File versioning (MinIO) | Complete | `internal/storage/` |
| File storage (filesystem) | Complete | `internal/storage/` |
| SHA256 checksums | Complete | Captured on upload |
| Revision comments | Complete | `revisions.comment` |
| User attribution | Complete | `revisions.created_by` |
@@ -93,7 +93,7 @@ CREATE TABLE revisions (
revision_number INTEGER NOT NULL,
properties JSONB NOT NULL DEFAULT '{}',
file_key TEXT,
file_version TEXT, -- MinIO version ID
file_version TEXT, -- storage version ID
file_checksum TEXT, -- SHA256
file_size BIGINT,
thumbnail_key TEXT,
@@ -283,7 +283,7 @@ Effort: Medium | Priority: Low | Risk: Low
**Changes:**
- Add thumbnail generation on file upload
- Store in MinIO at `thumbnails/{part_number}/rev{n}.png`
- Store at `thumbnails/{part_number}/rev{n}.png`
- Expose via `GET /api/items/{pn}/thumbnail/{rev}`
---
@@ -377,7 +377,7 @@ internal/
relationships.go # BOM repository
projects.go # Project repository
storage/
storage.go # MinIO file storage helpers
storage.go # File storage helpers
migrations/
001_initial.sql # Core schema
...
@@ -572,7 +572,7 @@ Reporting capabilities are absent. Basic reports (item counts, revision activity
| Feature | SOLIDWORKS PDM | Silo Status | Priority | Complexity |
|---------|---------------|-------------|----------|------------|
| File versioning | Automatic | Full (MinIO) | - | - |
| File versioning | Automatic | Full (filesystem) | - | - |
| File preview | Thumbnails, 3D preview | None | Medium | Complex |
| File conversion | PDF, DXF generation | None | Medium | Complex |
| Replication | Multi-site sync | None | Low | Complex |

View File

@@ -3,7 +3,7 @@
This guide covers two installation methods:
- **[Option A: Docker Compose](#option-a-docker-compose)** — self-contained stack with all services. Recommended for evaluation, small teams, and environments where Docker is the standard.
- **[Option B: Daemon Install](#option-b-daemon-install-systemd--external-services)** — systemd service with external PostgreSQL, MinIO, and optional LDAP/nginx. Recommended for production deployments integrated with existing infrastructure.
- **[Option B: Daemon Install](#option-b-daemon-install-systemd--external-services)** — systemd service with external PostgreSQL and optional LDAP/nginx. Files are stored on the local filesystem. Recommended for production deployments integrated with existing infrastructure.
Both methods produce the same result: a running Silo server with a web UI, REST API, and authentication.
@@ -48,7 +48,7 @@ Regardless of which method you choose:
## Option A: Docker Compose
A single Docker Compose file runs everything: PostgreSQL, MinIO, OpenLDAP, and Silo. An optional nginx container can be enabled for reverse proxying.
A single Docker Compose file runs everything: PostgreSQL, OpenLDAP, and Silo. Files are stored on the local filesystem. An optional nginx container can be enabled for reverse proxying.
### A.1 Prerequisites
@@ -80,7 +80,6 @@ The setup script generates credentials and configuration files:
It prompts for:
- Server domain (default: `localhost`)
- PostgreSQL password (auto-generated if you press Enter)
- MinIO credentials (auto-generated)
- OpenLDAP admin password and initial user (auto-generated)
- Silo local admin account (fallback when LDAP is unavailable)
@@ -106,7 +105,7 @@ Wait for all services to become healthy:
docker compose -f deployments/docker-compose.allinone.yaml ps
```
You should see `silo-postgres`, `silo-minio`, `silo-openldap`, and `silo-api` all in a healthy state.
You should see `silo-postgres`, `silo-openldap`, and `silo-api` all in a healthy state.
View logs:
@@ -124,7 +123,7 @@ docker compose -f deployments/docker-compose.allinone.yaml logs -f silo
# Health check
curl http://localhost:8080/health
# Readiness check (includes database and storage connectivity)
# Readiness check (includes database connectivity)
curl http://localhost:8080/ready
```
@@ -226,7 +225,7 @@ The Silo container is rebuilt from the updated source. Database migrations in `m
## Option B: Daemon Install (systemd + External Services)
This method runs Silo as a systemd service on a dedicated host, connecting to externally managed PostgreSQL, MinIO, and optionally LDAP services.
This method runs Silo as a systemd service on a dedicated host, connecting to externally managed PostgreSQL and optionally LDAP services. Files are stored on the local filesystem.
### B.1 Architecture Overview
@@ -240,21 +239,22 @@ This method runs Silo as a systemd service on a dedicated host, connecting to ex
│ ┌───────▼────────┐ │
│ │ silod │ │
│ │ (API server) │ │
└──┬─────────┬───┘
└─────┼─────────┼──────┘
┌─────────────┐ ┌─────────────────
│ PostgreSQL 16│ │ MinIO (S3)
:5432 │ │ :9000 API │
└──────────────┘ │ :9001 Console
└──────────────────┘
│ Files: /opt/ │
│ │ silo/data │ │
│ └──────┬─────────┘
─────────────────────
┌───────────▼──┐
│ PostgreSQL 16
│ :5432 │
└──────────────┘
```
### B.2 Prerequisites
- Linux host (Debian/Ubuntu or RHEL/Fedora/AlmaLinux)
- Root or sudo access
- Network access to your PostgreSQL and MinIO servers
- Network access to your PostgreSQL server
The setup script installs Go and other build dependencies automatically.
@@ -281,26 +281,6 @@ Verify:
psql -h YOUR_PG_HOST -U silo -d silo -c 'SELECT 1'
```
#### MinIO
Install MinIO and create a bucket and service account:
- [MinIO quickstart](https://min.io/docs/minio/linux/index.html)
```bash
# Using the MinIO client (mc):
mc alias set local http://YOUR_MINIO_HOST:9000 minioadmin minioadmin
mc mb local/silo-files
mc admin user add local silouser YOUR_MINIO_SECRET
mc admin policy attach local readwrite --user silouser
```
Verify:
```bash
curl -I http://YOUR_MINIO_HOST:9000/minio/health/live
```
#### LDAP / FreeIPA (Optional)
For LDAP authentication, you need an LDAP server with user and group entries. Options:
@@ -339,10 +319,10 @@ The script:
4. Clones the repository
5. Creates the environment file template
To override the default service hostnames:
To override the default database hostname:
```bash
SILO_DB_HOST=db.example.com SILO_MINIO_HOST=s3.example.com sudo -E bash scripts/setup-host.sh
SILO_DB_HOST=db.example.com sudo -E bash scripts/setup-host.sh
```
### B.5 Configure Credentials
@@ -357,10 +337,6 @@ sudo nano /etc/silo/silod.env
# Database
SILO_DB_PASSWORD=your-database-password
# MinIO
SILO_MINIO_ACCESS_KEY=silouser
SILO_MINIO_SECRET_KEY=your-minio-secret
# Authentication
SILO_SESSION_SECRET=generate-a-long-random-string
SILO_ADMIN_USERNAME=admin
@@ -379,7 +355,7 @@ Review the server configuration:
sudo nano /etc/silo/config.yaml
```
Update `database.host`, `storage.endpoint`, `server.base_url`, and authentication settings for your environment. See [CONFIGURATION.md](CONFIGURATION.md) for all options.
Update `database.host`, `storage.filesystem.root_dir`, `server.base_url`, and authentication settings for your environment. See [CONFIGURATION.md](CONFIGURATION.md) for all options.
### B.6 Deploy
@@ -412,10 +388,10 @@ sudo /opt/silo/src/scripts/deploy.sh --restart-only
sudo /opt/silo/src/scripts/deploy.sh --status
```
To override the target host or database host:
To override the target host:
```bash
SILO_DEPLOY_TARGET=silo.example.com SILO_DB_HOST=db.example.com sudo -E scripts/deploy.sh
SILO_DEPLOY_TARGET=silo.example.com sudo -E scripts/deploy.sh
```
### B.7 Set Up Nginx and TLS

View File

@@ -0,0 +1,485 @@
# .kc Server-Side Metadata Integration
**Status:** Draft
**Date:** February 2026
---
## 1. Purpose
When a `.kc` file is committed to Silo, the server extracts and indexes the `silo/` directory contents so that metadata is queryable, diffable, and streamable without downloading the full file. This document specifies the server-side processing pipeline, database storage, API endpoints, and SSE events that support the Create viewport widgets defined in [SILO_VIEWPORT.md](SILO_VIEWPORT.md).
The core principle: **the `.kc` file is the transport format; Silo is the index.** The `silo/` directory entries are extracted into database columns on commit and packed back into the ZIP on checkout. The server never modifies the FreeCAD standard zone (`Document.xml`, `.brp` files, `thumbnails/`).
---
## 2. Commit Pipeline
When a `.kc` file is uploaded via `POST /api/items/{partNumber}/file`, the server runs an extraction pipeline before returning success.
### 2.1 Pipeline Steps
```
Client uploads .kc file
|
v
+-----------------------------+
| 1. Store file to disk | (existing behavior -- unchanged)
| items/{pn}/rev{N}.kc |
+-----------------------------+
|
v
+-----------------------------+
| 2. Open ZIP, read silo/ |
| Parse each entry |
+-----------------------------+
|
v
+-----------------------------+
| 3. Validate manifest.json |
| - UUID matches item |
| - kc_version supported |
| - revision_hash present |
+-----------------------------+
|
v
+-----------------------------+
| 4. Index metadata |
| - Upsert item_metadata |
| - Upsert dependencies |
| - Append history entry |
| - Snapshot approvals |
| - Register macros |
| - Register job defs |
+-----------------------------+
|
v
+-----------------------------+
| 5. Broadcast SSE events |
| - revision.created |
| - metadata.updated |
| - bom.changed (if deps |
| differ from previous) |
+-----------------------------+
|
v
Return 201 Created
```
### 2.2 Validation Rules
| Check | Failure response |
|-------|-----------------|
| `silo/manifest.json` missing | `400 Bad Request` -- file is `.fcstd` not `.kc` |
| `manifest.uuid` doesn't match item's UUID | `409 Conflict` -- wrong item |
| `manifest.kc_version` > server's supported version | `422 Unprocessable` -- client newer than server |
| `manifest.revision_hash` matches current head | `200 OK` (no-op, file unchanged) |
| Any `silo/` JSON fails to parse | `422 Unprocessable` with path and parse error |
If validation fails, the blob is still stored (the user uploaded it), but no metadata indexing occurs. The item's revision is created with a `metadata_error` flag so the web UI can surface the problem.
### 2.3 Backward Compatibility
Plain `.fcstd` files (no `silo/` directory) continue to work exactly as today -- stored on disk, revision created, no metadata extraction. The pipeline short-circuits at step 2 when no `silo/` directory is found.
---
## 3. Database Schema
### 3.1 `item_metadata` Table
Stores the indexed contents of `silo/metadata.json` as structured JSONB, searchable and filterable via the existing item query endpoints.
```sql
CREATE TABLE item_metadata (
item_id UUID PRIMARY KEY REFERENCES items(id) ON DELETE CASCADE,
schema_name TEXT,
tags TEXT[] NOT NULL DEFAULT '{}',
lifecycle_state TEXT NOT NULL DEFAULT 'draft',
fields JSONB NOT NULL DEFAULT '{}',
kc_version TEXT,
manifest_uuid UUID,
silo_instance TEXT,
revision_hash TEXT,
updated_at TIMESTAMPTZ DEFAULT now(),
updated_by TEXT
);
CREATE INDEX idx_item_metadata_tags ON item_metadata USING GIN (tags);
CREATE INDEX idx_item_metadata_lifecycle ON item_metadata (lifecycle_state);
CREATE INDEX idx_item_metadata_fields ON item_metadata USING GIN (fields);
```
On commit, the server upserts this row from `silo/manifest.json` and `silo/metadata.json`. The `fields` column contains the schema-driven key-value pairs exactly as they appear in the JSON.
### 3.2 `item_dependencies` Table
Stores the indexed contents of `silo/dependencies.json`. Replaces the BOM for assembly relationships that originate from the CAD model.
```sql
CREATE TABLE item_dependencies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
parent_item_id UUID REFERENCES items(id) ON DELETE CASCADE,
child_uuid UUID NOT NULL,
child_part_number TEXT,
child_revision INTEGER,
quantity DECIMAL,
label TEXT,
relationship TEXT NOT NULL DEFAULT 'component',
revision_number INTEGER NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_item_deps_parent ON item_dependencies (parent_item_id);
CREATE INDEX idx_item_deps_child ON item_dependencies (child_uuid);
```
This table complements the existing `relationships` table. The `relationships` table is the server-authoritative BOM (editable via the web UI and API). The `item_dependencies` table is the CAD-authoritative record extracted from the file. BOM merge (per [BOM_MERGE.md](BOM_MERGE.md)) reconciles the two.
### 3.3 `item_approvals` Table
Stores the indexed contents of `silo/approvals.json`. Server-authoritative -- the `.kc` snapshot is a read cache.
```sql
CREATE TABLE item_approvals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
item_id UUID REFERENCES items(id) ON DELETE CASCADE,
eco_number TEXT,
state TEXT NOT NULL DEFAULT 'draft',
updated_at TIMESTAMPTZ DEFAULT now(),
updated_by TEXT
);
CREATE TABLE approval_signatures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
approval_id UUID REFERENCES item_approvals(id) ON DELETE CASCADE,
username TEXT NOT NULL,
role TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
signed_at TIMESTAMPTZ,
comment TEXT
);
```
These tables exist independent of `.kc` commits -- approvals are created and managed through the web UI and API. On `.kc` checkout, the current approval state is serialized into `silo/approvals.json` for offline display.
### 3.4 `item_macros` Table
Registers macros from `silo/macros/` for server-side discoverability and the future Macro Store module.
```sql
CREATE TABLE item_macros (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
item_id UUID REFERENCES items(id) ON DELETE CASCADE,
filename TEXT NOT NULL,
trigger TEXT NOT NULL DEFAULT 'manual',
content TEXT NOT NULL,
revision_number INTEGER NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(item_id, filename)
);
```
---
## 4. API Endpoints
These endpoints serve the viewport widgets in Create. All are under `/api/items/{partNumber}` and follow the existing auth model.
### 4.1 Metadata
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/metadata` | viewer | Get indexed metadata (schema fields, tags, lifecycle) |
| `PUT` | `/metadata` | editor | Update metadata fields from client |
| `PATCH` | `/metadata/lifecycle` | editor | Transition lifecycle state |
| `PATCH` | `/metadata/tags` | editor | Add/remove tags |
**`GET /api/items/{partNumber}/metadata`**
Returns the indexed metadata for viewport display. This is the fast path -- reads from `item_metadata` rather than downloading and parsing the `.kc` ZIP.
```json
{
"schema_name": "mechanical-part-v2",
"lifecycle_state": "draft",
"tags": ["structural", "aluminum"],
"fields": {
"material": "6061-T6",
"finish": "anodized",
"weight_kg": 0.34,
"category": "bracket"
},
"manifest": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"silo_instance": "https://silo.example.com",
"revision_hash": "a1b2c3d4e5f6",
"kc_version": "1.0"
},
"updated_at": "2026-02-13T20:30:00Z",
"updated_by": "joseph"
}
```
**`PUT /api/items/{partNumber}/metadata`**
Accepts a partial update of schema fields. The server merges into the existing `fields` JSONB. This is the write-back path for the Metadata Editor widget.
```json
{
"fields": {
"material": "7075-T6",
"weight_kg": 0.31
}
}
```
The server validates field names against the schema descriptor. Unknown fields are rejected with `422`.
**`PATCH /api/items/{partNumber}/metadata/lifecycle`**
Transitions lifecycle state. The server validates the transition is permitted (e.g., `draft` -> `review` is allowed, `released` -> `draft` is not without admin override).
```json
{ "state": "review" }
```
### 4.2 Dependencies
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/dependencies` | viewer | Get CAD-extracted dependency list |
| `GET` | `/dependencies/resolve` | viewer | Resolve UUIDs to current part numbers and file status |
**`GET /api/items/{partNumber}/dependencies`**
Returns the raw dependency list from the last `.kc` commit.
**`GET /api/items/{partNumber}/dependencies/resolve`**
Returns the dependency list with each UUID resolved to its current part number, revision, and whether the file exists on disk. This is what the Dependency Table widget calls to populate the status column.
```json
{
"links": [
{
"uuid": "660e8400-...",
"part_number": "KC-BRK-0042",
"label": "Base Plate",
"revision": 2,
"quantity": 1,
"resolved": true,
"file_available": true
},
{
"uuid": "770e8400-...",
"part_number": "KC-HDW-0108",
"label": "M6 SHCS",
"revision": 1,
"quantity": 4,
"resolved": true,
"file_available": true
},
{
"uuid": "880e8400-...",
"part_number": null,
"label": "Cover Panel",
"revision": 1,
"quantity": 1,
"resolved": false,
"file_available": false
}
]
}
```
### 4.3 Approvals
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/approvals` | viewer | Get current approval state |
| `POST` | `/approvals` | editor | Create ECO / start approval workflow |
| `POST` | `/approvals/{id}/sign` | editor | Sign (approve/reject) |
These endpoints power the Approvals Viewer widget. The viewer is read-only in Create -- sign actions happen in the web UI, but the API exists for both.
### 4.4 Macros
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/macros` | viewer | List registered macros |
| `GET` | `/macros/{filename}` | viewer | Get macro source |
Read-only server-side. Macros are authored in Create and committed inside the `.kc`. The server indexes them for discoverability in the future Macro Store.
### 4.5 Existing Endpoints (unchanged)
The viewport widgets also consume these existing endpoints:
| Widget | Endpoint | Purpose |
|--------|----------|---------|
| History Viewer | `GET /api/items/{pn}/revisions` | Full revision list |
| History Viewer | `GET /api/items/{pn}/revisions/compare` | Property diff |
| Job Viewer | `GET /api/jobs?item={pn}&definition={name}&limit=1` | Last job run |
| Job Viewer | `POST /api/jobs` | Trigger job |
| Job Viewer | `GET /api/jobs/{id}/logs` | Job log |
| Manifest Viewer | `GET /api/items/{pn}` | Item details (UUID, etc.) |
No changes needed to these -- they already exist and return the data the widgets need.
---
## 5. Checkout Pipeline
When a client downloads a `.kc` via `GET /api/items/{partNumber}/file`, the server packs current server-side state into the `silo/` directory before serving the file. This ensures the client always gets the latest metadata, even if it was edited via the web UI since the last commit.
### 5.1 Pipeline Steps
```
Client requests file download
|
v
+-----------------------------+
| 1. Read .kc from disk |
+-----------------------------+
|
v
+-----------------------------+
| 2. Pack silo/ from DB |
| - manifest.json (item) |
| - metadata.json (index) |
| - history.json (revs) |
| - approvals.json (ECO) |
| - dependencies.json |
| - macros/ (index) |
| - jobs/ (job defs) |
+-----------------------------+
|
v
+-----------------------------+
| 3. Replace silo/ in ZIP |
| Remove old entries |
| Write packed entries |
+-----------------------------+
|
v
Stream .kc to client
```
### 5.2 Packing Rules
| `silo/` entry | Source | Notes |
|---------------|--------|-------|
| `manifest.json` | `item_metadata` + `items` table | UUID from item, revision_hash from latest revision |
| `metadata.json` | `item_metadata.fields` + tags + lifecycle | Serialized from indexed columns |
| `history.json` | `revisions` table | Last 20 revisions for this item |
| `approvals.json` | `item_approvals` + `approval_signatures` | Current ECO state, omitted if no active ECO |
| `dependencies.json` | `item_dependencies` | Current revision's dependency list |
| `macros/*.py` | `item_macros` | All registered macros |
| `jobs/*.yaml` | `job_definitions` filtered by item type | Job definitions matching this item's trigger filters |
### 5.3 Caching
Packing the `silo/` directory on every download has a cost. To mitigate:
- **ETag header**: The response includes an ETag computed from the revision number + metadata `updated_at`. If the client sends `If-None-Match`, the server can return `304 Not Modified`.
- **Lazy packing**: If the `.kc` blob's `silo/manifest.json` revision_hash matches the current head *and* `item_metadata.updated_at` is older than the blob's upload time, skip repacking entirely -- the blob is already current.
---
## 6. SSE Events
The viewport widgets subscribe to SSE for live updates. These events are broadcast when server-side metadata changes, whether via `.kc` commit, web UI edit, or API call.
| Event | Payload | Trigger |
|-------|---------|---------|
| `metadata.updated` | `{part_number, changed_fields[], lifecycle_state, updated_by}` | Metadata PUT/PATCH |
| `metadata.lifecycle` | `{part_number, from_state, to_state, updated_by}` | Lifecycle transition |
| `metadata.tags` | `{part_number, added[], removed[]}` | Tag add/remove |
| `approval.created` | `{part_number, eco_number, state}` | ECO created |
| `approval.signed` | `{part_number, eco_number, user, role, status}` | Approver action |
| `approval.completed` | `{part_number, eco_number, final_state}` | All approvers acted |
| `dependencies.changed` | `{part_number, added[], removed[], changed[]}` | Dependency diff on commit |
Existing events (`revision.created`, `job.*`, `bom.changed`) continue to work as documented in [SPECIFICATION.md](SPECIFICATION.md) and [WORKERS.md](WORKERS.md).
### 6.1 Widget Subscription Map
| Viewport widget | Subscribes to |
|-----------------|---------------|
| Manifest Viewer | -- (read-only, no live updates) |
| Metadata Editor | `metadata.updated`, `metadata.lifecycle`, `metadata.tags` |
| History Viewer | `revision.created` |
| Approvals Viewer | `approval.created`, `approval.signed`, `approval.completed` |
| Dependency Table | `dependencies.changed` |
| Job Viewer | `job.created`, `job.progress`, `job.completed`, `job.failed` |
| Macro Editor | -- (local-only until committed) |
---
## 7. Web UI Integration
The Silo web UI also benefits from indexed metadata. These are additions to existing pages, not new pages.
### 7.1 Items Page
The item detail panel gains a **Metadata** tab (alongside Main, Properties, Revisions, BOM, Where Used) showing the schema-driven form from `GET /api/items/{pn}/metadata`. Editable for editors.
### 7.2 Items List
New filterable columns: `lifecycle_state`, `tags`. The existing search endpoint gains metadata-aware filtering:
```
GET /api/items?lifecycle=released&tag=aluminum
GET /api/items/search?q=bracket&lifecycle=draft
```
### 7.3 Approvals Page
A new page accessible from the top navigation (visible when a future `approvals` module is enabled). Lists all active ECOs with their approval progress.
---
## 8. Migration
### 8.1 Database Migration
A single migration adds the `item_metadata`, `item_dependencies`, `item_approvals`, `approval_signatures`, and `item_macros` tables. Existing items have no metadata rows -- they're created on first `.kc` commit or via `PUT /api/items/{pn}/metadata`.
### 8.2 Backfill
For items that already have `.kc` files stored on disk (committed before this feature), an admin endpoint re-runs the extraction pipeline:
```
POST /api/admin/reindex-metadata
```
This iterates all items with `.kc` files, opens each ZIP, and indexes the `silo/` contents. Idempotent -- safe to run multiple times.
---
## 9. Implementation Order
| Phase | Server work | Supports client phase |
|-------|------------|----------------------|
| 1 | `item_metadata` table + `GET/PUT /metadata` + commit extraction | SILO_VIEWPORT Phase 1-2 (Manifest, Metadata) |
| 2 | Pack `silo/` on checkout + ETag caching | SILO_VIEWPORT Phase 1-3 |
| 3 | `item_dependencies` table + `/dependencies/resolve` | SILO_VIEWPORT Phase 5 (Dependency Table) |
| 4 | `item_macros` table + `/macros` endpoints | SILO_VIEWPORT Phase 6 (Macro Editor) |
| 5 | `item_approvals` tables + `/approvals` endpoints | SILO_VIEWPORT Phase 7 (Approvals Viewer) |
| 6 | SSE events for metadata/approvals/dependencies | SILO_VIEWPORT Phase 8 (Live integration) |
| 7 | Web UI metadata tab + list filters | Independent of client |
Phases 1-2 are prerequisite for the viewport to work with live data. Phases 3-6 can be built in parallel with client widget development. Phase 7 is web-UI-only and independent.
---
## 10. References
- [SILO_VIEWPORT.md](SILO_VIEWPORT.md) -- Client-side viewport widget specification
- [KC_SPECIFICATION.md](KC_SPECIFICATION.md) -- .kc file format specification
- [SPECIFICATION.md](SPECIFICATION.md) -- Silo server API reference
- [BOM_MERGE.md](BOM_MERGE.md) -- BOM merge rules (dependency reconciliation)
- [WORKERS.md](WORKERS.md) -- Job queue (job viewer data source)
- [MODULES.md](MODULES.md) -- Module system (approval module gating)
- [ROADMAP.md](ROADMAP.md) -- Platform roadmap tiers

View File

@@ -0,0 +1,745 @@
# Module System Specification
**Status:** Draft
**Last Updated:** 2026-02-14
---
## 1. Purpose
Silo's module system defines the boundary between required infrastructure and optional capabilities. Each module groups a set of API endpoints, UI views, and configuration parameters. Modules can be enabled or disabled at runtime by administrators via the web UI, and clients can query which modules are active to adapt their feature set.
The goal: after initial deployment (where `config.yaml` sets database, storage, and server bind), all further operational configuration happens through the admin settings UI. The YAML file becomes the bootstrap; the database becomes the runtime source of truth.
---
## 2. Module Registry
### 2.1 Required Modules
These cannot be disabled. They define what Silo *is*.
| Module ID | Name | Description |
|-----------|------|-------------|
| `core` | Core PDM | Items, revisions, files, BOM, search, import/export, part number generation |
| `schemas` | Schemas | Part numbering schema parsing, segment management, form descriptors |
| `storage` | Storage | Filesystem storage |
### 2.2 Optional Modules
| Module ID | Name | Default | Description |
|-----------|------|---------|-------------|
| `auth` | Authentication | `true` | Local, LDAP, OIDC authentication and RBAC |
| `projects` | Projects | `true` | Project management and item tagging |
| `audit` | Audit | `true` | Audit logging, completeness scoring |
| `odoo` | Odoo ERP | `false` | Odoo integration (config, sync-log, push/pull) |
| `freecad` | Create Integration | `true` | URI scheme, executable path, client settings |
| `jobs` | Job Queue | `false` | Async compute jobs, runner management |
| `dag` | Dependency DAG | `false` | Feature DAG sync, validation states, interference detection |
### 2.3 Module Dependencies
Some modules require others to function:
| Module | Requires |
|--------|----------|
| `dag` | `jobs` |
| `jobs` | `auth` (runner tokens) |
| `odoo` | `auth` |
When enabling a module, its dependencies are validated. The server rejects enabling `dag` without `jobs`. Disabling a module that others depend on shows a warning listing dependents.
---
## 3. Endpoint-to-Module Mapping
### 3.1 `core` (required)
```
# Health
GET /health
GET /ready
# Items
GET /api/items
GET /api/items/search
GET /api/items/by-uuid/{uuid}
GET /api/items/export.csv
GET /api/items/template.csv
GET /api/items/export.ods
GET /api/items/template.ods
POST /api/items
POST /api/items/import
POST /api/items/import.ods
GET /api/items/{partNumber}
PUT /api/items/{partNumber}
DELETE /api/items/{partNumber}
# Revisions
GET /api/items/{partNumber}/revisions
GET /api/items/{partNumber}/revisions/compare
GET /api/items/{partNumber}/revisions/{revision}
POST /api/items/{partNumber}/revisions
PATCH /api/items/{partNumber}/revisions/{revision}
POST /api/items/{partNumber}/revisions/{revision}/rollback
# Files
GET /api/items/{partNumber}/files
GET /api/items/{partNumber}/file
GET /api/items/{partNumber}/file/{revision}
POST /api/items/{partNumber}/file
POST /api/items/{partNumber}/files
DELETE /api/items/{partNumber}/files/{fileId}
PUT /api/items/{partNumber}/thumbnail
POST /api/uploads/presign
# BOM
GET /api/items/{partNumber}/bom
GET /api/items/{partNumber}/bom/expanded
GET /api/items/{partNumber}/bom/flat
GET /api/items/{partNumber}/bom/cost
GET /api/items/{partNumber}/bom/where-used
GET /api/items/{partNumber}/bom/export.csv
GET /api/items/{partNumber}/bom/export.ods
POST /api/items/{partNumber}/bom
POST /api/items/{partNumber}/bom/import
POST /api/items/{partNumber}/bom/merge
PUT /api/items/{partNumber}/bom/{childPartNumber}
DELETE /api/items/{partNumber}/bom/{childPartNumber}
# .kc Metadata
GET /api/items/{partNumber}/metadata
PUT /api/items/{partNumber}/metadata
PATCH /api/items/{partNumber}/metadata/lifecycle
PATCH /api/items/{partNumber}/metadata/tags
# .kc Dependencies
GET /api/items/{partNumber}/dependencies
GET /api/items/{partNumber}/dependencies/resolve
# .kc Macros
GET /api/items/{partNumber}/macros
GET /api/items/{partNumber}/macros/{filename}
# Part Number Generation
POST /api/generate-part-number
# Sheets
POST /api/sheets/diff
# Settings & Modules (admin)
GET /api/modules
GET /api/admin/settings
GET /api/admin/settings/{module}
PUT /api/admin/settings/{module}
POST /api/admin/settings/{module}/test
```
### 3.2 `schemas` (required)
```
GET /api/schemas
GET /api/schemas/{name}
GET /api/schemas/{name}/form
POST /api/schemas/{name}/segments/{segment}/values
PUT /api/schemas/{name}/segments/{segment}/values/{code}
DELETE /api/schemas/{name}/segments/{segment}/values/{code}
```
### 3.3 `storage` (required)
No dedicated endpoints — storage is consumed internally by file upload/download in `core`. Exposed through admin settings for connection status visibility.
### 3.4 `auth`
```
# Public (login flow)
GET /login
POST /login
POST /logout
GET /auth/oidc
GET /auth/callback
# Authenticated
GET /api/auth/me
GET /api/auth/tokens
POST /api/auth/tokens
DELETE /api/auth/tokens/{id}
# Web UI
GET /settings (account info, tokens)
POST /settings/tokens
POST /settings/tokens/{id}/revoke
```
When `auth` is disabled, all routes are open and a synthetic `dev` admin user is injected (current behavior).
### 3.5 `projects`
```
GET /api/projects
GET /api/projects/{code}
GET /api/projects/{code}/items
GET /api/projects/{code}/sheet.ods
POST /api/projects
PUT /api/projects/{code}
DELETE /api/projects/{code}
# Item-project tagging
GET /api/items/{partNumber}/projects
POST /api/items/{partNumber}/projects
DELETE /api/items/{partNumber}/projects/{code}
```
When disabled: project tag endpoints return `404`, project columns are hidden in UI list views, project filter is removed from item search.
### 3.6 `audit`
```
GET /api/audit/completeness
GET /api/audit/completeness/{partNumber}
```
When disabled: audit log table continues to receive writes (it's part of core middleware), but the completeness scoring endpoints and the Audit page in the web UI are hidden. Future: retention policies, export, and compliance reporting endpoints live here.
### 3.7 `odoo`
```
GET /api/integrations/odoo/config
GET /api/integrations/odoo/sync-log
PUT /api/integrations/odoo/config
POST /api/integrations/odoo/test-connection
POST /api/integrations/odoo/sync/push/{partNumber}
POST /api/integrations/odoo/sync/pull/{odooId}
```
### 3.8 `freecad`
No dedicated API endpoints currently. Configures URI scheme and executable path used by the web UI's "Open in Create" links and by CLI operations. Future: client configuration distribution endpoint.
### 3.9 `jobs`
```
# User-facing
GET /api/jobs
GET /api/jobs/{jobID}
GET /api/jobs/{jobID}/logs
POST /api/jobs
POST /api/jobs/{jobID}/cancel
# Job definitions
GET /api/job-definitions
GET /api/job-definitions/{name}
POST /api/job-definitions/reload
# Runner management (admin)
GET /api/runners
POST /api/runners
DELETE /api/runners/{runnerID}
# Runner-facing (runner token auth)
POST /api/runner/heartbeat
POST /api/runner/claim
PUT /api/runner/jobs/{jobID}/progress
POST /api/runner/jobs/{jobID}/complete
POST /api/runner/jobs/{jobID}/fail
POST /api/runner/jobs/{jobID}/log
PUT /api/runner/jobs/{jobID}/dag
```
### 3.10 `dag`
```
GET /api/items/{partNumber}/dag
GET /api/items/{partNumber}/dag/forward-cone/{nodeKey}
GET /api/items/{partNumber}/dag/dirty
PUT /api/items/{partNumber}/dag
POST /api/items/{partNumber}/dag/mark-dirty/{nodeKey}
```
---
## 4. Disabled Module Behavior
When a module is disabled:
1. **API routes** registered by that module return `404 Not Found` with body `{"error": "module '<id>' is not enabled"}`.
2. **Web UI** hides the module's navigation entry, page, and any inline UI elements (e.g., project tags on item cards).
3. **SSE events** from the module are not broadcast.
4. **Background goroutines** (e.g., job timeout sweeper, runner heartbeat checker) are not started.
5. **Database tables** are not dropped — they remain for re-enablement. No data loss on disable/enable cycle.
Implementation: each module's route group is wrapped in a middleware check:
```go
func RequireModule(id string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !modules.IsEnabled(id) {
http.Error(w, `{"error":"module '`+id+`' is not enabled"}`, 404)
return
}
next.ServeHTTP(w, r)
})
}
}
```
---
## 5. Configuration Persistence
### 5.1 Precedence
```
Environment variables (highest — always wins, secrets live here)
Database overrides (admin UI writes here)
config.yaml (lowest — bootstrap defaults)
```
### 5.2 Database Table
```sql
-- Migration 014_settings.sql
CREATE TABLE settings_overrides (
key TEXT PRIMARY KEY, -- dotted path: "auth.ldap.enabled"
value JSONB NOT NULL, -- typed value
updated_by TEXT NOT NULL, -- username
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE module_state (
module_id TEXT PRIMARY KEY, -- "auth", "projects", etc.
enabled BOOLEAN NOT NULL,
updated_by TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```
### 5.3 Load Sequence
On startup:
1. Parse `config.yaml` into Go config struct.
2. Query `settings_overrides` — merge each key into the struct using dotted path resolution.
3. Apply environment variable overrides (existing `SILO_*` vars).
4. Query `module_state` — override default enabled/disabled from YAML.
5. Validate module dependencies.
6. Register only enabled modules' route groups.
7. Start only enabled modules' background goroutines.
### 5.4 Runtime Updates
When an admin saves settings via `PUT /api/admin/settings/{module}`:
1. Validate the payload against the module's config schema.
2. Write changed keys to `settings_overrides`.
3. Update `module_state` if `enabled` changed.
4. Apply changes to the in-memory config (hot reload where safe).
5. Broadcast `settings.changed` SSE event with `{module, enabled, changed_keys}`.
6. For changes that require restart (e.g., `server.port`, `database.*`), return a `restart_required: true` flag in the response. The UI shows a banner.
### 5.5 What Requires Restart
| Config Area | Hot Reload | Restart Required |
|-------------|-----------|------------------|
| Module enable/disable | Yes | No |
| `auth.*` provider toggles | Yes | No |
| `auth.cors.allowed_origins` | Yes | No |
| `odoo.*` connection settings | Yes | No |
| `freecad.*` | Yes | No |
| `jobs.*` timeouts, directory | Yes | No |
| `server.host`, `server.port` | No | Yes |
| `database.*` | No | Yes |
| `storage.*` | No | Yes |
| `schemas.directory` | No | Yes |
---
## 6. Public Module Discovery Endpoint
```
GET /api/modules
```
**No authentication required.** Clients need this pre-login to know whether OIDC is available, whether projects exist, etc.
### 6.1 Response
```json
{
"modules": {
"core": {
"enabled": true,
"required": true,
"name": "Core PDM",
"version": "0.2"
},
"schemas": {
"enabled": true,
"required": true,
"name": "Schemas"
},
"storage": {
"enabled": true,
"required": true,
"name": "Storage"
},
"auth": {
"enabled": true,
"required": false,
"name": "Authentication",
"config": {
"local_enabled": true,
"ldap_enabled": true,
"oidc_enabled": true,
"oidc_issuer_url": "https://keycloak.example.com/realms/silo"
}
},
"projects": {
"enabled": true,
"required": false,
"name": "Projects"
},
"audit": {
"enabled": true,
"required": false,
"name": "Audit"
},
"odoo": {
"enabled": false,
"required": false,
"name": "Odoo ERP"
},
"freecad": {
"enabled": true,
"required": false,
"name": "Create Integration",
"config": {
"uri_scheme": "silo"
}
},
"jobs": {
"enabled": false,
"required": false,
"name": "Job Queue"
},
"dag": {
"enabled": false,
"required": false,
"name": "Dependency DAG",
"depends_on": ["jobs"]
}
},
"server": {
"version": "0.2",
"read_only": false
}
}
```
The `config` sub-object exposes only public, non-secret metadata needed by clients. Never includes passwords, tokens, or secret keys.
---
## 7. Admin Settings Endpoints
### 7.1 Get All Settings
```
GET /api/admin/settings
Authorization: Bearer <admin token>
```
Returns full config grouped by module with secrets redacted:
```json
{
"core": {
"server": {
"host": "0.0.0.0",
"port": 8080,
"base_url": "https://silo.example.com",
"read_only": false
}
},
"schemas": {
"directory": "/etc/silo/schemas",
"default": "kindred-rd"
},
"storage": {
"backend": "filesystem",
"filesystem": {
"root_dir": "/var/lib/silo/data"
},
"status": "connected"
},
"database": {
"host": "postgres",
"port": 5432,
"name": "silo",
"user": "silo",
"password": "****",
"sslmode": "disable",
"max_connections": 10,
"status": "connected"
},
"auth": {
"enabled": true,
"session_secret": "****",
"local": { "enabled": true },
"ldap": {
"enabled": true,
"url": "ldaps://ipa.example.com",
"base_dn": "dc=kindred,dc=internal",
"user_search_dn": "cn=users,cn=accounts,dc=kindred,dc=internal",
"bind_password": "****",
"role_mapping": { "...": "..." }
},
"oidc": {
"enabled": true,
"issuer_url": "https://keycloak.example.com/realms/silo",
"client_id": "silo",
"client_secret": "****",
"redirect_url": "https://silo.example.com/auth/callback"
},
"cors": { "allowed_origins": ["https://silo.example.com"] }
},
"projects": { "enabled": true },
"audit": { "enabled": true },
"odoo": { "enabled": false, "url": "", "database": "", "username": "" },
"freecad": { "uri_scheme": "silo", "executable": "" },
"jobs": {
"enabled": false,
"directory": "/etc/silo/jobdefs",
"runner_timeout": 90,
"job_timeout_check": 30,
"default_priority": 100
},
"dag": { "enabled": false }
}
```
### 7.2 Get Module Settings
```
GET /api/admin/settings/{module}
```
Returns just the module's config block.
### 7.3 Update Module Settings
```
PUT /api/admin/settings/{module}
Content-Type: application/json
{
"enabled": true,
"ldap": {
"enabled": true,
"url": "ldaps://ipa.example.com"
}
}
```
**Response:**
```json
{
"updated": ["auth.ldap.enabled", "auth.ldap.url"],
"restart_required": false
}
```
### 7.4 Test Connectivity
```
POST /api/admin/settings/{module}/test
```
Available for modules with external connections:
| Module | Test Action |
|--------|------------|
| `storage` | Verify filesystem storage directory is accessible |
| `auth` (ldap) | Attempt LDAP bind with configured credentials |
| `auth` (oidc) | Fetch OIDC discovery document from issuer URL |
| `odoo` | Attempt XML-RPC connection to Odoo |
**Response:**
```json
{
"success": true,
"message": "LDAP bind successful",
"latency_ms": 42
}
```
---
## 8. Config YAML Changes
The existing `config.yaml` gains a `modules` section. Existing top-level keys remain for backward compatibility — the module system reads from both locations.
```yaml
# Existing keys (unchanged, still work)
server:
host: "0.0.0.0"
port: 8080
database:
host: postgres
port: 5432
name: silo
user: silo
password: silodev
sslmode: disable
storage:
backend: filesystem
filesystem:
root_dir: /var/lib/silo/data
schemas:
directory: /etc/silo/schemas
auth:
enabled: true
session_secret: change-me
local:
enabled: true
# New: explicit module toggles (optional, defaults shown)
modules:
projects:
enabled: true
audit:
enabled: true
odoo:
enabled: false
freecad:
enabled: true
uri_scheme: silo
jobs:
enabled: false
directory: /etc/silo/jobdefs
runner_timeout: 90
job_timeout_check: 30
default_priority: 100
dag:
enabled: false
```
If a module is not listed under `modules:`, its default enabled state from Section 2.2 applies. The `auth.enabled` field continues to control the `auth` module (no duplication under `modules:`).
---
## 9. SSE Events
```
settings.changed {module, enabled, changed_keys[], updated_by}
```
Broadcast on any admin settings change. The web UI listens for this to:
- Show/hide navigation entries when modules are toggled.
- Display a "Settings updated by another admin" toast.
- Show a "Restart required" banner when flagged.
---
## 10. Web UI — Admin Settings Page
The Settings page (`/settings`) is restructured into sections:
### 10.1 Existing (unchanged)
- **Account** — username, display name, email, auth source, role badge.
- **API Tokens** — create, list, revoke.
### 10.2 New: Module Configuration (admin only)
Visible only to admin users. Each module gets a collapsible card:
```
┌─────────────────────────────────────────────────────┐
│ [toggle] Authentication [status] │
├─────────────────────────────────────────────────────┤
│ │
│ ── Local Auth ──────────────────────────────────── │
│ Enabled: [toggle] │
│ │
│ ── LDAP / FreeIPA ──────────────────────────────── │
│ Enabled: [toggle] │
│ URL: [ldaps://ipa.example.com ] │
│ Base DN: [dc=kindred,dc=internal ] [Test] │
│ │
│ ── OIDC / Keycloak ────────────────────────────── │
│ Enabled: [toggle] │
│ Issuer URL: [https://keycloak.example.com] [Test] │
│ Client ID: [silo ] │
│ │
│ ── CORS ────────────────────────────────────────── │
│ Allowed Origins: [tag input] │
│ │
│ [Save] │
└─────────────────────────────────────────────────────┘
```
Module cards for required modules (`core`, `schemas`, `storage`) show their status and config but have no enable/disable toggle.
Status indicators per module:
| Status | Badge | Meaning |
|--------|-------|---------|
| Active | `green` | Enabled and operational |
| Disabled | `overlay1` | Toggled off |
| Error | `red` | Enabled but connectivity or config issue |
| Setup Required | `yellow` | Enabled but missing required config (e.g., LDAP URL empty) |
### 10.3 Infrastructure Section (admin, read-only)
Shows connection status for required infrastructure:
- **Database** — host, port, name, connection pool usage, status badge.
- **Storage** — endpoint, bucket, SSL, status badge.
These are read-only in the UI (setup-only via YAML/env). The "Test" button is available to verify connectivity.
---
## 11. Implementation Order
1. **Migration 014**`settings_overrides` and `module_state` tables.
2. **Config loader refactor** — YAML → DB merge → env override pipeline.
3. **Module registry** — Go struct defining all modules with metadata, dependencies, defaults.
4. **`GET /api/modules`** — public endpoint, no auth.
5. **`RequireModule` middleware** — gate route groups by module state.
6. **Admin settings API**`GET/PUT /api/admin/settings/{module}`, test endpoints.
7. **Web UI settings page** — module cards with toggles, config forms, test buttons.
8. **SSE integration**`settings.changed` event broadcast.
---
## 12. Future Considerations
- **Module manifest format** — per ROADMAP.md, each module will eventually declare routes, views, hooks, and permissions via a manifest. This spec covers the runtime module registry; the manifest format is TBD.
- **Custom modules** — third-party modules that register against the endpoint registry. Requires the manifest contract and a plugin loading mechanism.
- **Per-module permissions** — beyond the current role hierarchy, modules may define fine-grained scopes (e.g., `jobs:admin`, `dag:write`).
- **Location & Inventory module** — when the Location/Inventory API is implemented (tables already exist), it becomes a new optional module.
- **Notifications module** — per ROADMAP.md Tier 1, notifications/subscriptions will be a dedicated module.
---
## 13. References
- [CONFIGURATION.md](CONFIGURATION.md) — Current config reference
- [ROADMAP.md](ROADMAP.md) — Module manifest, API endpoint registry
- [AUTH.md](AUTH.md) — Authentication architecture
- [WORKERS.md](WORKERS.md) — Job queue system
- [DAG.md](DAG.md) — Dependency DAG specification
- [SPECIFICATION.md](SPECIFICATION.md) — Full endpoint listing

View File

@@ -88,7 +88,7 @@ Everything depends on these. They define what Silo *is*.
| Component | Description | Status |
|-----------|-------------|--------|
| **Core Silo** | Part/assembly storage, version control, auth, base REST API | Complete |
| **.kc Format Spec** | File format contract between Create and Silo | Not Started |
| **.kc Format Spec** | File format contract between Create and Silo | Complete |
| **API Endpoint Registry** | Module discovery, dynamic UI rendering, health checks | Not Started |
| **Web UI Shell** | App launcher, breadcrumbs, view framework, module rendering | Partial |
| **Python Scripting Engine** | Server-side hook execution, module extension point | Not Started |
@@ -313,7 +313,7 @@ For full SOLIDWORKS PDM comparison tables, see [GAP_ANALYSIS.md Appendix C](GAP_
- Rollback functionality
#### File Management
- MinIO integration with versioning
- Filesystem-based file storage
- File upload/download via REST API
- SHA256 checksums for integrity
- Storage path: `items/{partNumber}/rev{N}.FCStd`
@@ -377,8 +377,8 @@ For full SOLIDWORKS PDM comparison tables, see [GAP_ANALYSIS.md Appendix C](GAP_
## Appendix B: Phase 1 Detailed Tasks
### 1.1 MinIO Integration -- COMPLETE
- [x] MinIO service configured in Docker Compose
### 1.1 File Storage -- COMPLETE
- [x] Filesystem storage backend
- [x] File upload via REST API
- [x] File download via REST API (latest and by revision)
- [x] SHA256 checksums on upload

View File

@@ -0,0 +1,899 @@
# Solver Service Specification
**Status:** Draft
**Last Updated:** 2026-02-19
**Depends on:** KCSolve Phase 1 (PR #297), Phase 2 (PR #298)
---
## 1. Overview
The solver service extends Silo's job queue system with assembly constraint solving capabilities. It enables server-side solving of assemblies stored in Silo, with results streamed back to clients in real time via SSE.
This specification describes how the existing KCSolve client-side API (C++ library + pybind11 `kcsolve` module) integrates with Silo's worker infrastructure to provide headless, asynchronous constraint solving.
### 1.1 Goals
1. **Offload solving** -- Move heavy solve operations off the user's machine to server workers.
2. **Batch validation** -- Automatically validate assemblies on commit (e.g. check for over-constrained systems).
3. **Solver selection** -- Allow the server to run different solvers than the client (e.g. a more thorough solver for validation, a fast one for interactive editing).
4. **Standalone execution** -- Solver workers can run without a full FreeCAD installation, using just the `kcsolve` Python module and the `.kc` file.
### 1.2 Non-Goals
- **Interactive drag** -- Real-time drag solving stays client-side (latency-sensitive).
- **Geometry processing** -- Workers don't compute geometry; they receive pre-extracted constraint graphs.
- **Solver development** -- Writing new solver backends is out of scope; this spec covers the transport and execution layer.
---
## 2. Architecture
```
┌─────────────────────┐
│ Kindred Create │
│ (FreeCAD client) │
└───────┬──────────────┘
│ 1. POST /api/solver/jobs
│ (SolveContext JSON)
│ 4. GET /api/events (SSE)
│ solver.progress, solver.completed
┌─────────────────────┐
│ Silo Server │
│ (silod) │
│ │
│ solver module │
│ REST + SSE + queue │
└───────┬──────────────┘
│ 2. POST /api/runner/claim
│ 3. POST /api/runner/jobs/{id}/complete
┌─────────────────────┐
│ Solver Runner │
│ (silorunner) │
│ │
│ kcsolve module │
│ OndselAdapter │
│ Python solvers │
└─────────────────────┘
```
### 2.1 Components
| Component | Role | Deployment |
|-----------|------|------------|
| **Silo server** | Job queue management, REST API, SSE broadcast, result storage | Existing `silod` binary |
| **Solver runner** | Claims solver jobs, executes `kcsolve`, reports results | New runner tag `solver` on existing `silorunner` |
| **kcsolve module** | Python/C++ solver library (Phase 1+2) | Installed on runner nodes |
| **Create client** | Submits jobs, receives results via SSE | Existing FreeCAD client |
### 2.2 Module Registration
The solver service is a Silo module with ID `solver`, gated behind the existing module system:
```yaml
# config.yaml
modules:
solver:
enabled: true
```
It depends on the `jobs` module being enabled. All solver endpoints return `404` with `{"error": "module not enabled"}` when disabled.
---
## 3. Data Model
### 3.1 SolveContext JSON Schema
The `SolveContext` is the input to a solve operation. Currently it exists only as a C++ struct and pybind11 binding with no serialization. Phase 3 adds JSON serialization to enable server transport.
```json
{
"api_version": 1,
"parts": [
{
"id": "Part001",
"placement": {
"position": [0.0, 0.0, 0.0],
"quaternion": [1.0, 0.0, 0.0, 0.0]
},
"mass": 1.0,
"grounded": true
},
{
"id": "Part002",
"placement": {
"position": [100.0, 0.0, 0.0],
"quaternion": [1.0, 0.0, 0.0, 0.0]
},
"mass": 1.0,
"grounded": false
}
],
"constraints": [
{
"id": "Joint001",
"part_i": "Part001",
"marker_i": {
"position": [50.0, 0.0, 0.0],
"quaternion": [1.0, 0.0, 0.0, 0.0]
},
"part_j": "Part002",
"marker_j": {
"position": [0.0, 0.0, 0.0],
"quaternion": [1.0, 0.0, 0.0, 0.0]
},
"type": "Revolute",
"params": [],
"limits": [],
"activated": true
}
],
"motions": [],
"simulation": null,
"bundle_fixed": false
}
```
**Field reference:** See [KCSolve Python API](../reference/kcsolve-python.md) for full field documentation. The JSON schema maps 1:1 to the Python/C++ types.
**Enum serialization:** Enums serialize as strings matching their Python names (e.g. `"Revolute"`, `"Success"`, `"Redundant"`).
**Transform shorthand:** The `placement` and `marker_*` fields use the `Transform` struct: `position` is `[x, y, z]`, `quaternion` is `[w, x, y, z]`.
**Constraint.Limit:**
```json
{
"kind": "RotationMin",
"value": -1.5708,
"tolerance": 1e-9
}
```
**MotionDef:**
```json
{
"kind": "Rotational",
"joint_id": "Joint001",
"marker_i": "",
"marker_j": "",
"rotation_expr": "2*pi*t",
"translation_expr": ""
}
```
**SimulationParams:**
```json
{
"t_start": 0.0,
"t_end": 2.0,
"h_out": 0.04,
"h_min": 1e-9,
"h_max": 1.0,
"error_tol": 1e-6
}
```
### 3.2 SolveResult JSON Schema
```json
{
"status": "Success",
"placements": [
{
"id": "Part002",
"placement": {
"position": [50.0, 0.0, 0.0],
"quaternion": [0.707, 0.0, 0.707, 0.0]
}
}
],
"dof": 1,
"diagnostics": [
{
"constraint_id": "Joint003",
"kind": "Redundant",
"detail": "6 DOF removed by Joint003 are already constrained"
}
],
"num_frames": 0
}
```
### 3.3 Solver Job Record
Solver jobs are stored in the existing `jobs` table. The solver-specific data is in the `args` and `result` JSONB columns.
**Job args (input):**
```json
{
"solver": "ondsel",
"operation": "solve",
"context": { /* SolveContext JSON */ },
"item_part_number": "ASM-001",
"revision_number": 3
}
```
**Operation types:**
| Operation | Description | Requires simulation? |
|-----------|-------------|---------------------|
| `solve` | Static equilibrium solve | No |
| `diagnose` | Constraint analysis only (no placement update) | No |
| `kinematic` | Time-domain kinematic simulation | Yes |
**Job result (output):**
```json
{
"result": { /* SolveResult JSON */ },
"solver_name": "OndselSolver (Lagrangian)",
"solver_version": "1.0",
"solve_time_ms": 127.4
}
```
---
## 4. REST API
All endpoints are prefixed with `/api/solver/` and gated behind `RequireModule("solver")`.
### 4.1 Submit Solve Job
```
POST /api/solver/jobs
Authorization: Bearer silo_...
Content-Type: application/json
{
"solver": "ondsel",
"operation": "solve",
"context": { /* SolveContext */ },
"priority": 50
}
```
**Optional fields:**
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `solver` | string | `""` (default solver) | Solver name from registry |
| `operation` | string | `"solve"` | `solve`, `diagnose`, or `kinematic` |
| `context` | object | required | SolveContext JSON |
| `priority` | int | `50` | Lower = higher priority |
| `item_part_number` | string | `null` | Silo item reference (for result association) |
| `revision_number` | int | `null` | Revision that generated this context |
| `callback_url` | string | `null` | Webhook URL for completion notification |
**Response `201 Created`:**
```json
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"created_at": "2026-02-19T18:30:00Z"
}
```
**Error responses:**
| Code | Condition |
|------|-----------|
| `400` | Invalid SolveContext (missing required fields, unknown enum values) |
| `401` | Not authenticated |
| `404` | Module not enabled |
| `422` | Unknown solver name, invalid operation |
### 4.2 Get Job Status
```
GET /api/solver/jobs/{jobID}
```
**Response `200 OK`:**
```json
{
"job_id": "550e8400-...",
"status": "completed",
"operation": "solve",
"solver": "ondsel",
"priority": 50,
"item_part_number": "ASM-001",
"revision_number": 3,
"runner_id": "runner-01",
"runner_name": "solver-worker-01",
"created_at": "2026-02-19T18:30:00Z",
"claimed_at": "2026-02-19T18:30:01Z",
"completed_at": "2026-02-19T18:30:02Z",
"result": {
"result": { /* SolveResult */ },
"solver_name": "OndselSolver (Lagrangian)",
"solve_time_ms": 127.4
}
}
```
### 4.3 List Solver Jobs
```
GET /api/solver/jobs?status=completed&item=ASM-001&limit=20&offset=0
```
**Query parameters:**
| Param | Type | Description |
|-------|------|-------------|
| `status` | string | Filter by status: `pending`, `claimed`, `running`, `completed`, `failed` |
| `item` | string | Filter by item part number |
| `operation` | string | Filter by operation type |
| `solver` | string | Filter by solver name |
| `limit` | int | Page size (default 20, max 100) |
| `offset` | int | Pagination offset |
**Response `200 OK`:**
```json
{
"jobs": [ /* array of job objects */ ],
"total": 42,
"limit": 20,
"offset": 0
}
```
### 4.4 Cancel Job
```
POST /api/solver/jobs/{jobID}/cancel
```
Only `pending` and `claimed` jobs can be cancelled. Running jobs must complete or time out.
**Response `200 OK`:**
```json
{
"job_id": "550e8400-...",
"status": "cancelled"
}
```
### 4.5 Get Solver Registry
```
GET /api/solver/solvers
```
Returns available solvers on registered runners. Runners report their solver capabilities during heartbeat.
**Response `200 OK`:**
```json
{
"solvers": [
{
"name": "ondsel",
"display_name": "OndselSolver (Lagrangian)",
"deterministic": true,
"supported_joints": [
"Coincident", "Fixed", "Revolute", "Cylindrical",
"Slider", "Ball", "Screw", "Gear", "RackPinion",
"Parallel", "Perpendicular", "Angle", "Planar",
"Concentric", "PointOnLine", "PointInPlane",
"LineInPlane", "Tangent", "DistancePointPoint",
"DistanceCylSph", "Universal"
],
"runner_count": 2
}
],
"default_solver": "ondsel"
}
```
---
## 5. Server-Sent Events
Solver jobs emit events on the existing `/api/events` SSE stream.
### 5.1 Event Types
| Event | Payload | When |
|-------|---------|------|
| `solver.created` | `{job_id, operation, solver, item_part_number}` | Job submitted |
| `solver.claimed` | `{job_id, runner_id, runner_name}` | Runner starts work |
| `solver.progress` | `{job_id, progress, message}` | Progress update (0-100) |
| `solver.completed` | `{job_id, status, dof, diagnostics_count, solve_time_ms}` | Job succeeded |
| `solver.failed` | `{job_id, error_message}` | Job failed |
### 5.2 Example Stream
```
event: solver.created
data: {"job_id":"abc-123","operation":"solve","solver":"ondsel","item_part_number":"ASM-001"}
event: solver.claimed
data: {"job_id":"abc-123","runner_id":"r1","runner_name":"solver-worker-01"}
event: solver.progress
data: {"job_id":"abc-123","progress":50,"message":"Building constraint system..."}
event: solver.completed
data: {"job_id":"abc-123","status":"Success","dof":3,"diagnostics_count":1,"solve_time_ms":127.4}
```
### 5.3 Client Integration
The Create client subscribes to the SSE stream and updates the Assembly workbench UI:
1. **Silo viewport widget** shows job status indicator (pending/running/done/failed)
2. On `solver.completed`, the client can fetch the full result via `GET /api/solver/jobs/{id}` and apply placements
3. On `solver.failed`, the client shows the error in the report panel
4. Diagnostic results (redundant/conflicting constraints) surface in the constraint tree
---
## 6. Runner Integration
### 6.1 Runner Requirements
Solver runners are standard `silorunner` instances with the `solver` tag. They require:
- Python 3.11+ with `kcsolve` module installed
- `libKCSolve.so` and solver backend libraries (e.g. `libOndselSolver.so`)
- Network access to the Silo server
No FreeCAD installation is required. The runner operates on pre-extracted `SolveContext` JSON.
### 6.2 Runner Registration
```bash
# Register a solver runner (admin)
curl -X POST https://silo.example.com/api/runners \
-H "Authorization: Bearer admin_token" \
-d '{"name":"solver-01","tags":["solver"]}'
# Response includes one-time token
{"id":"uuid","token":"silo_runner_xyz..."}
```
### 6.3 Runner Heartbeat
Runners report solver capabilities during heartbeat:
```json
POST /api/runner/heartbeat
{
"capabilities": {
"solvers": ["ondsel"],
"api_version": 1,
"python_version": "3.11.11"
}
}
```
### 6.4 Runner Execution Flow
```python
#!/usr/bin/env python3
"""Solver runner entry point."""
import json
import kcsolve
def execute_solve_job(args: dict) -> dict:
"""Execute a solver job from parsed args."""
solver_name = args.get("solver", "")
operation = args.get("operation", "solve")
ctx_dict = args["context"]
# Deserialize SolveContext from JSON
ctx = kcsolve.SolveContext.from_dict(ctx_dict)
# Load solver
solver = kcsolve.load(solver_name)
if solver is None:
raise ValueError(f"Unknown solver: {solver_name!r}")
# Execute operation
if operation == "solve":
result = solver.solve(ctx)
elif operation == "diagnose":
diags = solver.diagnose(ctx)
result = kcsolve.SolveResult()
result.diagnostics = diags
elif operation == "kinematic":
result = solver.run_kinematic(ctx)
else:
raise ValueError(f"Unknown operation: {operation!r}")
# Serialize result
return {
"result": result.to_dict(),
"solver_name": solver.name(),
"solver_version": "1.0",
}
```
### 6.5 Standalone Process Mode
For minimal deployments, the runner can invoke a standalone solver process:
```bash
echo '{"solver":"ondsel","operation":"solve","context":{...}}' | \
python3 -m kcsolve.runner
```
The `kcsolve.runner` module reads JSON from stdin, executes the solve, and writes the result JSON to stdout. Exit code 0 = success, non-zero = failure with error JSON on stderr.
---
## 7. Job Definitions
### 7.1 Manual Solve Job
Triggered by the client when the user requests a server-side solve:
```yaml
job:
name: assembly-solve
version: 1
description: "Solve assembly constraints on server"
trigger:
type: manual
scope:
type: assembly
compute:
type: solver
command: solver-run
runner:
tags: [solver]
timeout: 300
max_retries: 1
priority: 50
```
### 7.2 Commit-Time Validation
Automatically validates assembly constraints when a new revision is committed:
```yaml
job:
name: assembly-validate
version: 1
description: "Validate assembly constraints on commit"
trigger:
type: revision_created
filter:
item_type: assembly
scope:
type: assembly
compute:
type: solver
command: solver-diagnose
args:
operation: diagnose
runner:
tags: [solver]
timeout: 120
max_retries: 2
priority: 75
```
### 7.3 Kinematic Simulation
Server-side kinematic simulation for assemblies with motion definitions:
```yaml
job:
name: assembly-kinematic
version: 1
description: "Run kinematic simulation"
trigger:
type: manual
scope:
type: assembly
compute:
type: solver
command: solver-kinematic
args:
operation: kinematic
runner:
tags: [solver]
timeout: 1800
max_retries: 0
priority: 100
```
---
## 8. SolveContext Extraction
When a solver job is triggered by a revision commit (rather than a direct context submission), the server or runner must extract a `SolveContext` from the `.kc` file.
### 8.1 Extraction via Headless Create
For full-fidelity extraction that handles geometry classification:
```bash
create --console -e "
import kcsolve_extract
kcsolve_extract.extract_and_solve('input.kc', 'output.json', solver='ondsel')
"
```
This requires a full Create installation on the runner and uses the Assembly module's existing adapter layer to build `SolveContext` from document objects.
### 8.2 Extraction from .kc Silo Directory
For lightweight extraction without FreeCAD, the constraint graph can be stored in the `.kc` archive's `silo/` directory during commit:
```
silo/solver/context.json # Pre-extracted SolveContext
silo/solver/result.json # Last solve result (if any)
```
The client extracts the `SolveContext` locally before committing the `.kc` file. The server reads it from the archive, avoiding the need for geometry processing on the runner.
**Commit-time packing** (client side):
```python
# In the Assembly workbench commit hook:
ctx = assembly_object.build_solve_context()
kc_archive.write("silo/solver/context.json", ctx.to_json())
```
**Runner-side extraction:**
```python
import zipfile, json
with zipfile.ZipFile("assembly.kc") as zf:
ctx_json = json.loads(zf.read("silo/solver/context.json"))
```
---
## 9. Database Schema
### 9.1 Migration
The solver module uses the existing `jobs` table. One new table is added for result caching:
```sql
-- Migration: 020_solver_results.sql
CREATE TABLE solver_results (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
item_id UUID NOT NULL REFERENCES items(id) ON DELETE CASCADE,
revision_number INTEGER NOT NULL,
job_id UUID REFERENCES jobs(id) ON DELETE SET NULL,
operation TEXT NOT NULL, -- 'solve', 'diagnose', 'kinematic'
solver_name TEXT NOT NULL,
status TEXT NOT NULL, -- SolveStatus string
dof INTEGER,
diagnostics JSONB DEFAULT '[]',
placements JSONB DEFAULT '[]',
num_frames INTEGER DEFAULT 0,
solve_time_ms DOUBLE PRECISION,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(item_id, revision_number, operation)
);
CREATE INDEX idx_solver_results_item ON solver_results(item_id);
CREATE INDEX idx_solver_results_status ON solver_results(status);
```
The `UNIQUE(item_id, revision_number, operation)` constraint means each revision has at most one result per operation type. Re-running overwrites the previous result.
### 9.2 Result Association
When a solver job completes, the server:
1. Stores the full result in the `jobs.result` JSONB column (standard job result)
2. Upserts a row in `solver_results` for quick lookup by item/revision
3. Broadcasts `solver.completed` SSE event
---
## 10. Configuration
### 10.1 Server Config
```yaml
# config.yaml
modules:
solver:
enabled: true
default_solver: "ondsel"
max_context_size_mb: 10 # Reject oversized SolveContext payloads
default_timeout: 300 # Default job timeout (seconds)
auto_diagnose_on_commit: true # Auto-submit diagnose job on revision commit
```
### 10.2 Environment Variables
| Variable | Description |
|----------|-------------|
| `SILO_SOLVER_ENABLED` | Override module enabled state |
| `SILO_SOLVER_DEFAULT` | Default solver name |
### 10.3 Runner Config
```yaml
# runner.yaml
server_url: https://silo.example.com
token: silo_runner_xyz...
tags: [solver]
solver:
kcsolve_path: /opt/create/lib # LD_LIBRARY_PATH for kcsolve.so
python: /opt/create/bin/python3
max_concurrent: 2 # Parallel job slots per runner
```
---
## 11. Security
### 11.1 Authentication
All solver endpoints use the existing Silo authentication:
- **User endpoints** (`/api/solver/jobs`): Session or API token, requires `viewer` role to read, `editor` role to submit
- **Runner endpoints** (`/api/runner/...`): Runner token authentication (existing)
### 11.2 Input Validation
The server validates SolveContext JSON before queuing:
- Maximum payload size (configurable, default 10 MB)
- Required fields present (`parts`, `constraints`)
- Enum values are valid strings
- Transform arrays have correct length (position: 3, quaternion: 4)
- No duplicate part or constraint IDs
### 11.3 Runner Isolation
Solver runners execute untrusted constraint data. Mitigations:
- Runners should run in containers or sandboxed environments
- Python solver registration (`kcsolve.register_solver()`) is disabled in runner mode
- Solver execution has a configurable timeout (killed on expiry)
- Result size is bounded (large kinematic simulations are truncated)
---
## 12. Client SDK
### 12.1 Python Client
The existing `silo-client` package is extended with solver methods:
```python
from silo_client import SiloClient
client = SiloClient("https://silo.example.com", token="silo_...")
# Submit a solve job
import kcsolve
ctx = kcsolve.SolveContext()
# ... build context ...
job = client.solver.submit(ctx.to_dict(), solver="ondsel")
print(job.id, job.status) # "pending"
# Poll for completion
result = client.solver.wait(job.id, timeout=60)
print(result.status) # "Success"
# Or use SSE for real-time updates
for event in client.solver.stream(job.id):
print(event.type, event.data)
# Query results for an item
results = client.solver.results("ASM-001")
```
### 12.2 Create Workbench Integration
The Assembly workbench adds a "Solve on Server" command:
```python
# CommandSolveOnServer.py (sketch)
def activated(self):
assembly = get_active_assembly()
ctx = assembly.build_solve_context()
# Submit to Silo
from silo_client import get_client
client = get_client()
job = client.solver.submit(ctx.to_dict())
# Subscribe to SSE for updates
self.watch_job(job.id)
def on_solver_completed(self, job_id, result):
# Apply placements back to assembly
assembly = get_active_assembly()
for pr in result["placements"]:
assembly.set_part_placement(pr["id"], pr["placement"])
assembly.recompute()
```
---
## 13. Implementation Plan
### Phase 3a: JSON Serialization
Add `to_dict()` / `from_dict()` methods to all KCSolve types in the pybind11 module.
**Files to modify:**
- `src/Mod/Assembly/Solver/bindings/kcsolve_py.cpp` -- add dict conversion methods
**Verification:** `ctx.to_dict()` round-trips through `SolveContext.from_dict()`.
### Phase 3b: Server Endpoints
Add the solver module to the Silo server.
**Files to create (in silo repository):**
- `internal/modules/solver/solver.go` -- Module registration and config
- `internal/modules/solver/handlers.go` -- REST endpoint handlers
- `internal/modules/solver/events.go` -- SSE event definitions
- `migrations/020_solver_results.sql` -- Database migration
### Phase 3c: Runner Support
Add solver job execution to `silorunner`.
**Files to create:**
- `src/Mod/Assembly/Solver/bindings/runner.py` -- `kcsolve.runner` entry point
- Runner capability reporting during heartbeat
### Phase 3d: .kc Context Packing
Pack `SolveContext` into `.kc` archives on commit.
**Files to modify:**
- `mods/silo/freecad/silo_origin.py` -- Hook into commit to pack solver context
### Phase 3e: Client Integration
Add "Solve on Server" command to the Assembly workbench.
**Files to modify:**
- `mods/silo/freecad/` -- Solver client methods
- `src/Mod/Assembly/` -- Server solve command
---
## 14. Open Questions
1. **Context size limits** -- Large assemblies may produce multi-MB SolveContext JSON. Should we compress (gzip) or use a binary format (msgpack)?
2. **Result persistence** -- How long should solver results be retained? Per-revision (overwritten on next commit) or historical (keep all)?
3. **Kinematic frame storage** -- Kinematic simulations can produce thousands of frames. Store all frames in JSONB, or write to a separate file and reference it?
4. **Multi-solver comparison** -- Should the API support running the same context through multiple solvers and comparing results? Useful for Phase 4 (second solver validation).
5. **Webhook notifications** -- The `callback_url` field allows external integrations (e.g. CI). What authentication should the webhook use?
---
## 15. References
- [KCSolve Architecture](../architecture/ondsel-solver.md)
- [KCSolve Python API Reference](../reference/kcsolve-python.md)
- [INTER_SOLVER.md](../../INTER_SOLVER.md) -- Full pluggable solver spec
- [WORKERS.md](WORKERS.md) -- Worker/runner job system
- [SPECIFICATION.md](SPECIFICATION.md) -- Silo server specification
- [MODULES.md](MODULES.md) -- Module system

View File

@@ -37,7 +37,7 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb
┌─────────────────────────────────────────────────────────────┐
│ Silo Server (silod) │
│ - REST API (78 endpoints) │
│ - REST API (86 endpoints) │
│ - Authentication (local, LDAP, OIDC) │
│ - Schema parsing and validation │
│ - Part number generation engine │
@@ -49,9 +49,9 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb
┌───────────────┴───────────────┐
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────────┐
│ PostgreSQL │ │ MinIO
│ PostgreSQL │ │ Local Filesystem
│ (psql.example.internal)│ │ - File storage │
│ - Item metadata │ │ - Versioned objects
│ - Item metadata │ │ - Revision files
│ - Relationships │ │ - Thumbnails │
│ - Revision history │ │ │
│ - Auth / Sessions │ │ │
@@ -64,7 +64,7 @@ Silo treats **part numbering schemas as configuration, not code**. Multiple numb
| Component | Technology | Notes |
|-----------|------------|-------|
| Database | PostgreSQL 16 | Existing instance at psql.example.internal |
| File Storage | MinIO | S3-compatible, versioning enabled |
| File Storage | Local filesystem | Files stored under configurable root directory |
| CLI & API Server | Go (1.24) | chi/v5 router, pgx/v5 driver, zerolog |
| Authentication | Multi-backend | Local (bcrypt), LDAP/FreeIPA, OIDC/Keycloak |
| Sessions | PostgreSQL pgxstore | alexedwards/scs, 24h lifetime |
@@ -83,7 +83,7 @@ An **item** is the fundamental entity. Items have:
- **Properties** (key-value pairs, schema-defined and custom)
- **Relationships** to other items
- **Revisions** (append-only history)
- **Files** (optional, stored in MinIO)
- **Files** (optional, stored on the local filesystem)
- **Location** (optional physical inventory location)
### 3.2 Database Schema (Conceptual)
@@ -115,7 +115,7 @@ CREATE TABLE revisions (
item_id UUID REFERENCES items(id) NOT NULL,
revision_number INTEGER NOT NULL,
properties JSONB NOT NULL, -- all properties at this revision
file_version TEXT, -- MinIO version ID if applicable
file_version TEXT, -- storage version ID if applicable
created_at TIMESTAMPTZ DEFAULT now(),
created_by TEXT, -- user identifier (future: LDAP DN)
comment TEXT,
@@ -345,7 +345,7 @@ CAD workbench and spreadsheet extension implementations are maintained in separa
### 5.1 File Storage Strategy
Files are stored as whole objects in MinIO with versioning enabled. Storage path convention: `items/{partNumber}/rev{N}.ext`. SHA-256 checksums are captured on upload for integrity verification.
Files are stored on the local filesystem under a configurable root directory. Storage path convention: `items/{partNumber}/rev{N}.ext`. SHA-256 checksums are captured on upload for integrity verification.
Future option: exploded storage (unpack ZIP-based CAD archives for better diffing).
@@ -439,7 +439,7 @@ Revisions are created explicitly by user action (not automatic):
### 7.3 Revision vs. File Version
- **Revision**: Silo metadata revision (tracked in PostgreSQL)
- **File Version**: MinIO object version (automatic on upload)
- **File Version**: File on disk corresponding to a revision
A single Silo revision may span multiple file uploads during editing. Only committed revisions create formal revision records.
@@ -598,12 +598,12 @@ See [AUTH.md](AUTH.md) for full architecture details and [AUTH_USER_GUIDE.md](AU
## 11. API Design
### 11.1 REST Endpoints (78 Implemented)
### 11.1 REST Endpoints (86 Implemented)
```
# Health (no auth)
GET /health # Basic health check
GET /ready # Readiness (DB + MinIO)
GET /ready # Readiness (DB)
# Auth (no auth required)
GET /login # Login page
@@ -624,8 +624,8 @@ GET /api/auth/tokens # List user's API to
POST /api/auth/tokens # Create API token
DELETE /api/auth/tokens/{id} # Revoke API token
# Presigned Uploads (editor)
POST /api/uploads/presign # Get presigned MinIO upload URL [editor]
# Direct Uploads (editor)
POST /api/uploads/presign # Get upload URL [editor]
# Schemas (read: viewer, write: editor)
GET /api/schemas # List all schemas
@@ -697,6 +697,20 @@ POST /api/items/{partNumber}/bom/merge # Merge BOM from ODS
PUT /api/items/{partNumber}/bom/{childPartNumber} # Update BOM entry [editor]
DELETE /api/items/{partNumber}/bom/{childPartNumber} # Remove BOM entry [editor]
# .kc Metadata (read: viewer, write: editor)
GET /api/items/{partNumber}/metadata # Get indexed .kc metadata
PUT /api/items/{partNumber}/metadata # Update metadata fields [editor]
PATCH /api/items/{partNumber}/metadata/lifecycle # Transition lifecycle state [editor]
PATCH /api/items/{partNumber}/metadata/tags # Add/remove tags [editor]
# .kc Dependencies (viewer)
GET /api/items/{partNumber}/dependencies # List raw dependencies
GET /api/items/{partNumber}/dependencies/resolve # Resolve UUIDs to part numbers + file availability
# .kc Macros (viewer)
GET /api/items/{partNumber}/macros # List registered macros
GET /api/items/{partNumber}/macros/{filename} # Get macro source content
# Audit (viewer)
GET /api/audit/completeness # Item completeness scores
GET /api/audit/completeness/{partNumber} # Item detail breakdown
@@ -735,6 +749,139 @@ POST /api/inventory/{partNumber}/move
---
## 11.3 .kc File Integration
Silo supports the `.kc` file format — a ZIP archive that is a superset of FreeCAD's `.fcstd`. A `.kc` file contains everything an `.fcstd` does, plus a `silo/` directory with platform metadata.
#### Standard entries (preserved as-is)
`Document.xml`, `GuiDocument.xml`, BREP geometry files (`.brp`), `thumbnails/`
#### Silo entries (`silo/` directory)
| Path | Purpose |
|------|---------|
| `silo/manifest.json` | Instance origin, part UUID, revision hash, `.kc` schema version |
| `silo/metadata.json` | Custom schema field values, tags, lifecycle state |
| `silo/history.json` | Local revision log (server-generated on checkout) |
| `silo/dependencies.json` | Assembly link references by Silo UUID |
| `silo/macros/*.py` | Embedded macro scripts bound to this part |
#### Commit-time extraction
When a `.kc` file is uploaded via `POST /api/items/{partNumber}/file`, the server:
1. Opens the ZIP and scans for `silo/` entries
2. Parses `silo/manifest.json` and validates the UUID matches the item
3. Upserts `silo/metadata.json` fields into the `item_metadata` table
4. Replaces `silo/dependencies.json` entries in the `item_dependencies` table
5. Replaces `silo/macros/*.py` entries in the `item_macros` table
6. Broadcasts SSE events: `metadata.updated`, `dependencies.changed`, `macros.changed`
Extraction is best-effort — failures are logged as warnings but do not block the upload.
#### Checkout-time packing
When a `.kc` file is downloaded via `GET /api/items/{partNumber}/file/{revision}`, the server repacks the `silo/` directory with current database state:
- `silo/manifest.json` — current item UUID and metadata freshness
- `silo/metadata.json` — latest schema fields, tags, lifecycle state
- `silo/history.json` — last 20 revisions from the database
- `silo/dependencies.json` — current dependency list from `item_dependencies`
Non-silo ZIP entries are passed through unchanged. If the file is a plain `.fcstd` (no `silo/` directory), it is served as-is.
ETag caching: the server computes an ETag from `revision_number:metadata.updated_at` and returns `304 Not Modified` when the client's `If-None-Match` header matches.
#### Lifecycle state machine
The `lifecycle_state` field in `item_metadata` follows this state machine:
```
draft → review → released → obsolete
↑ ↓
└────────┘
```
Valid transitions are enforced by `PATCH /metadata/lifecycle`. Invalid transitions return `422 Unprocessable Entity`.
#### Metadata response shape
```json
{
"schema_name": "kindred-rd",
"lifecycle_state": "draft",
"tags": ["prototype", "v2"],
"fields": {"material": "AL6061", "finish": "anodized"},
"manifest": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"silo_instance": "silo.example.com",
"revision_hash": "abc123",
"kc_version": "1.0"
},
"updated_at": "2026-02-18T12:00:00Z",
"updated_by": "forbes"
}
```
#### Dependency response shape
```json
[
{
"uuid": "550e8400-...",
"part_number": "F01-0042",
"revision": 3,
"quantity": 4.0,
"label": "M5 Bolt",
"relationship": "component"
}
]
```
#### Resolved dependency response shape
```json
[
{
"uuid": "550e8400-...",
"part_number": "F01-0042",
"label": "M5 Bolt",
"revision": 3,
"quantity": 4.0,
"resolved": true,
"file_available": true
}
]
```
#### Macro list response shape
```json
[
{"filename": "validate_dims.py", "trigger": "manual", "revision_number": 5}
]
```
#### Macro detail response shape
```json
{
"filename": "validate_dims.py",
"trigger": "manual",
"content": "import FreeCAD\n...",
"revision_number": 5
}
```
#### Database tables (migration 018)
- `item_metadata` — schema fields, lifecycle state, tags, manifest info
- `item_dependencies` — parent/child UUID references with quantity and relationship type
- `item_macros` — filename, trigger type, source content, indexed per item
---
## 12. MVP Scope
### 12.1 Implemented
@@ -743,8 +890,8 @@ POST /api/inventory/{partNumber}/move
- [x] YAML schema parser for part numbering
- [x] Part number generation engine
- [x] CLI tool (`cmd/silo`)
- [x] API server (`cmd/silod`) with 78 endpoints
- [x] MinIO integration for file storage with versioning
- [x] API server (`cmd/silod`) with 86 endpoints
- [x] Filesystem-based file storage
- [x] BOM relationships (component, alternate, reference)
- [x] Multi-level BOM (recursive expansion with configurable depth)
- [x] Where-used queries (reverse parent lookup)
@@ -765,6 +912,12 @@ POST /api/inventory/{partNumber}/move
- [x] Audit logging and completeness scoring
- [x] CSRF protection (nosurf)
- [x] Fuzzy search
- [x] .kc file extraction pipeline (metadata, dependencies, macros indexed on commit)
- [x] .kc file packing on checkout (manifest, metadata, history, dependencies)
- [x] .kc metadata API (get, update fields, lifecycle transitions, tags)
- [x] .kc dependency API (list, resolve with file availability)
- [x] .kc macro API (list, get source content)
- [x] ETag caching for .kc file downloads
- [x] Property schema versioning framework
- [x] Docker Compose deployment (dev and prod)
- [x] systemd service and deployment scripts

View File

@@ -10,12 +10,12 @@
| Component | Status | Notes |
|-----------|--------|-------|
| PostgreSQL schema | Complete | 13 migrations applied |
| PostgreSQL schema | Complete | 18 migrations applied |
| YAML schema parser | Complete | Supports enum, serial, constant, string segments |
| Part number generator | Complete | Scoped sequences, category-based format |
| API server (`silod`) | Complete | 78 REST endpoints via chi/v5 |
| API server (`silod`) | Complete | 86 REST endpoints via chi/v5 |
| CLI tool (`silo`) | Complete | Item registration and management |
| MinIO file storage | Complete | Upload, download, versioning, checksums |
| Filesystem file storage | Complete | Upload, download, checksums |
| Revision control | Complete | Append-only history, rollback, comparison, status/labels |
| Project management | Complete | CRUD, many-to-many item tagging |
| CSV import/export | Complete | Dry-run validation, template generation |
@@ -29,7 +29,12 @@
| CSRF protection | Complete | nosurf on web forms |
| Fuzzy search | Complete | sahilm/fuzzy library |
| Web UI | Complete | React SPA (Vite + TypeScript), 6 pages, Catppuccin Mocha theme |
| File attachments | Complete | Presigned uploads, item file association, thumbnails |
| File attachments | Complete | Direct uploads, item file association, thumbnails |
| .kc extraction pipeline | Complete | Metadata, dependencies, macros indexed on commit |
| .kc checkout packing | Complete | Manifest, metadata, history, dependencies repacked on download |
| .kc metadata API | Complete | GET/PUT metadata, lifecycle transitions, tag management |
| .kc dependency API | Complete | List raw deps, resolve UUIDs to part numbers + file availability |
| .kc macro API | Complete | List macros, get source content by filename |
| Odoo ERP integration | Partial | Config and sync-log CRUD functional; push/pull are stubs |
| Docker Compose | Complete | Dev and production configurations |
| Deployment scripts | Complete | setup-host, deploy, init-db, setup-ipa-nginx |
@@ -56,7 +61,7 @@ FreeCAD workbench and LibreOffice Calc extension are maintained in separate repo
| Service | Host | Status |
|---------|------|--------|
| PostgreSQL | psql.example.internal:5432 | Running |
| MinIO | localhost:9000 (API) / :9001 (console) | Configured |
| File Storage | /opt/silo/data (filesystem) | Configured |
| Silo API | localhost:8080 | Builds successfully |
---
@@ -96,3 +101,8 @@ The schema defines 170 category codes across 10 groups:
| 011_item_files.sql | Item file attachments (item_files table, thumbnail_key column) |
| 012_bom_source.sql | BOM entry source tracking |
| 013_move_cost_sourcing_to_props.sql | Move sourcing_link and standard_cost from item columns to revision properties |
| 014_settings.sql | Settings overrides and module state tables |
| 015_jobs.sql | Job queue, runner, and job log tables |
| 016_dag.sql | Dependency DAG nodes and edges |
| 017_locations.sql | Location hierarchy and inventory tracking |
| 018_kc_metadata.sql | .kc metadata tables (item_metadata, item_dependencies, item_macros, item_approvals, approval_signatures) |

View File

@@ -337,7 +337,7 @@ Supporting files:
| File | Purpose |
|------|---------|
| `web/src/components/items/CategoryPicker.tsx` | Multi-stage domain/subcategory selector |
| `web/src/components/items/FileDropZone.tsx` | Drag-and-drop file upload with MinIO presigned URLs |
| `web/src/components/items/FileDropZone.tsx` | Drag-and-drop file upload |
| `web/src/components/items/TagInput.tsx` | Multi-select tag input for projects |
| `web/src/hooks/useFormDescriptor.ts` | Fetches and caches form descriptor from `/api/schemas/{name}/form` |
| `web/src/hooks/useFileUpload.ts` | Manages presigned URL upload flow |
@@ -421,7 +421,7 @@ Below the picker, the selected category is shown as a breadcrumb: `Fasteners
### FileDropZone
Handles drag-and-drop and click-to-browse file uploads with MinIO presigned URL flow.
Handles drag-and-drop and click-to-browse file uploads.
**Props**:
@@ -435,7 +435,7 @@ interface FileDropZoneProps {
interface PendingAttachment {
file: File;
objectKey: string; // MinIO key after upload
objectKey: string; // storage key after upload
uploadProgress: number; // 0-100
uploadStatus: 'pending' | 'uploading' | 'complete' | 'error';
error?: string;
@@ -462,7 +462,7 @@ Clicking the zone opens a hidden `<input type="file" multiple>`.
1. On file selection/drop, immediately request a presigned upload URL: `POST /api/uploads/presign` with `{ filename, content_type, size }`.
2. Backend returns `{ object_key, upload_url, expires_at }`.
3. `PUT` the file directly to the presigned MinIO URL using `XMLHttpRequest` (for progress tracking).
3. `PUT` the file directly to the presigned URL using `XMLHttpRequest` (for progress tracking).
4. On completion, update `PendingAttachment.uploadStatus` to `'complete'` and store the `object_key`.
5. The `object_key` is later sent to the item creation endpoint to associate the file.
@@ -589,10 +589,10 @@ Items 1-5 below are implemented. Item 4 (hierarchical categories) is resolved by
```
POST /api/uploads/presign
Request: { "filename": "bracket.FCStd", "content_type": "application/octet-stream", "size": 2400000 }
Response: { "object_key": "uploads/tmp/{uuid}/{filename}", "upload_url": "https://minio.../...", "expires_at": "2026-02-06T..." }
Response: { "object_key": "uploads/tmp/{uuid}/{filename}", "upload_url": "https://...", "expires_at": "2026-02-06T..." }
```
The Go handler generates a presigned PUT URL via the MinIO SDK. Objects are uploaded to a temporary prefix. On item creation, they're moved/linked to the item's permanent prefix.
The Go handler generates a presigned PUT URL for direct upload. Objects are uploaded to a temporary prefix. On item creation, they're moved/linked to the item's permanent prefix.
### 2. File Association -- IMPLEMENTED
@@ -612,7 +612,7 @@ Request: { "object_key": "uploads/tmp/{uuid}/thumb.png" }
Response: 204
```
Stores the thumbnail at `items/{item_id}/thumbnail.png` in MinIO. Updates `item.thumbnail_key` column.
Stores the thumbnail at `items/{item_id}/thumbnail.png` in storage. Updates `item.thumbnail_key` column.
### 4. Hierarchical Categories -- IMPLEMENTED (via Form Descriptor)

View File

@@ -34,7 +34,7 @@ silo/
│ ├── ods/ # ODS spreadsheet library
│ ├── partnum/ # Part number generation
│ ├── schema/ # YAML schema parsing
│ ├── storage/ # MinIO file storage
│ ├── storage/ # Filesystem storage
│ └── testutil/ # Test helpers
├── web/ # React SPA (Vite + TypeScript)
│ └── src/
@@ -55,7 +55,7 @@ silo/
See the **[Installation Guide](docs/INSTALL.md)** for complete setup instructions.
**Docker Compose (quickest — includes PostgreSQL, MinIO, OpenLDAP, and Silo):**
**Docker Compose (quickest — includes PostgreSQL, OpenLDAP, and Silo):**
```bash
./scripts/setup-docker.sh
@@ -65,7 +65,7 @@ docker compose -f deployments/docker-compose.allinone.yaml up -d
**Development (local Go + Docker services):**
```bash
make docker-up # Start PostgreSQL + MinIO in Docker
make docker-up # Start PostgreSQL in Docker
make run # Run silo locally with Go
```

43
icons/mappings/FCAD.csv Normal file
View File

@@ -0,0 +1,43 @@
#fce94f,#edd400,#c4a000,#302b00
#8ae234,#73d216,#4e9a06,#172a04
#fcaf3e,#f57900,#ce5c00,#321900
#729fcf,#3465a4,#204a87,#0b1521
#ad7fa8,#75507b,#5c3566,#171018
#e9b96e,#c17d11,#8f5902,#271903
#ef2929,#cc0000,#a40000,#280000
#34e0e2,#16d0d2,#06989a,#042a2a
#ffffff,#eeeeec,#d3d7cf,#babdb6
#888a85,#555753,#2e3436,#000000
#faff2b,#fff520,#fff110,#fff300
#ffe83f,#ffe100,#ffed00,#ffff00
#fcc217,#fdb616,#ffaa00,#ffa200
#c89600,#aa6c00,#af7d00,#a7873d
#cabd55,#e3d032,#c9830a,#231f0b
#2b2200
#ff5f00,#ff6200,#cf7008
#ff0000,#ff2600,#ef3535,#ff4c4c
#c91a1a,#d40000,#c51900,#a70202
#3d0000,#230b0b,#2e0000
#6dff00,#00ff00,#52ff00,#00fd00
#31d900,#00b800,#4bff54
#2e8207,#0f7d0f,#17230b,#162c02
#71b2f8,#89d5f8,#639ef0,#83a8d8
#c8e0f9,#c1e3f7,#c4d7eb,#b9cfe7
#379cfb,#89aedc,#528ac5,#5b86be
#637dca,#3977c3,#1e64ff
#0000ff,#0069ff,#0090ff,#0040ff
#0087ff,#0046ff,#005bff,#0066ff
#061aff,#3f3fff,#0061e6,#0099e5
#003ddd,#001ccc,#0619c0
#0019a3,#002795
#0c1522,#0f222f
#00e5ff,#01d6d6,#00899e,#0b1e23
#fafafa,#f8f8f8,#f7f7f7,#fafbf9
#f0f0f0,#f0f1f1,#eeeeee,#ededed
#e8e8e8,#e5e5e5,#e2e2e2
#dcdcdc,#d8d8d8,#d6d6d6,#d1d1d1
#cccccc,#c8c8c8,#ccd0c7,#bbbbbb
#b8b8b8,#a3a3a3,#9a9a9a,#999999
#897e7e,#666666,#5f5f5f
#505050,#4c4c4c,#403c3d,#3f3f3f
#e5007e,#bf3995,#260013
1 #fce94f #edd400 #c4a000 #302b00
2 #8ae234 #73d216 #4e9a06 #172a04
3 #fcaf3e #f57900 #ce5c00 #321900
4 #729fcf #3465a4 #204a87 #0b1521
5 #ad7fa8 #75507b #5c3566 #171018
6 #e9b96e #c17d11 #8f5902 #271903
7 #ef2929 #cc0000 #a40000 #280000
8 #34e0e2 #16d0d2 #06989a #042a2a
9 #ffffff #eeeeec #d3d7cf #babdb6
10 #888a85 #555753 #2e3436 #000000
11 #faff2b #fff520 #fff110 #fff300
12 #ffe83f #ffe100 #ffed00 #ffff00
13 #fcc217 #fdb616 #ffaa00 #ffa200
14 #c89600 #aa6c00 #af7d00 #a7873d
15 #cabd55 #e3d032 #c9830a #231f0b
16 #2b2200
17 #ff5f00 #ff6200 #cf7008
18 #ff0000 #ff2600 #ef3535 #ff4c4c
19 #c91a1a #d40000 #c51900 #a70202
20 #3d0000 #230b0b #2e0000
21 #6dff00 #00ff00 #52ff00 #00fd00
22 #31d900 #00b800 #4bff54
23 #2e8207 #0f7d0f #17230b #162c02
24 #71b2f8 #89d5f8 #639ef0 #83a8d8
25 #c8e0f9 #c1e3f7 #c4d7eb #b9cfe7
26 #379cfb #89aedc #528ac5 #5b86be
27 #637dca #3977c3 #1e64ff
28 #0000ff #0069ff #0090ff #0040ff
29 #0087ff #0046ff #005bff #0066ff
30 #061aff #3f3fff #0061e6 #0099e5
31 #003ddd #001ccc #0619c0
32 #0019a3 #002795
33 #0c1522 #0f222f
34 #00e5ff #01d6d6 #00899e #0b1e23
35 #fafafa #f8f8f8 #f7f7f7 #fafbf9
36 #f0f0f0 #f0f1f1 #eeeeee #ededed
37 #e8e8e8 #e5e5e5 #e2e2e2
38 #dcdcdc #d8d8d8 #d6d6d6 #d1d1d1
39 #cccccc #c8c8c8 #ccd0c7 #bbbbbb
40 #b8b8b8 #a3a3a3 #9a9a9a #999999
41 #897e7e #666666 #5f5f5f
42 #505050 #4c4c4c #403c3d #3f3f3f
43 #e5007e #bf3995 #260013

View File

@@ -0,0 +1,43 @@
#f9e2af,#f8c459,#bc8009,#664506
#a6e3a1,#6cd163,#359b2e,#1c5017
#fab387,#f77e33,#b44908,#6e2d04
#89b4fa,#307bf7,#0846b3,#052459
#cba6f7,#8a39ec,#5710ad,#290850
#f5c2e7,#e86ec6,#b11b87,#490b38
#f2cdcd,#d76363,#912424,#4c1313
#94e2d5,#54d1bc,#258e7e,#103b35
#cdd6f4,#7f849c,#6c7086,#585b70
#45475a,#313244,#1e1e2e,#11111b
#f9d791,#f9d487,#f8cf78,#f8ca69
#f9dea3,#f8ca69,#f8ca69,#f8ca69
#f8d07c,#f8d07c,#f8ca69,#f8ca69
#c28711,#ad7608,#b07809,#ebb547
#f9d487,#f8d17e,#d29926,#664506
#664506
#f7863f,#f7863f,#c35512
#e9aaaa,#e9aaaa,#f2cdcd,#f2cdcd
#df8383,#da6e6e,#cb5858,#9a2c2c
#581616,#4f1414,#4f1414
#89da82,#89da82,#89da82,#86d97f
#61c658,#47ad40,#a6e3a1
#308c29,#318e2a,#1c5017,#1c5017
#89b4fa,#89b4fa,#89b4fa,#89b4fa
#89b4fa,#89b4fa,#89b4fa,#89b4fa
#7cacfa,#89b4fa,#659df9,#679ef9
#78a9f9,#4f8ff8,#6aa0f9
#5190f8,#5190f8,#5190f8,#5190f8
#5190f8,#5190f8,#5190f8,#5190f8
#5693f8,#86b2fa,#3c83f7,#3b82f7
#347ef7,#266ee6,#2168de
#0845b0,#0841a6
#05255a,#052966
#74dac8,#49c1ad,#258d7d,#103b35
#b7bfdc,#afb6d2,#aab2cd,#b7bfdc
#8c92ab,#8e94ad,#8389a1,#7f849c
#7b8098,#797e95,#777c93
#73778e,#70748a,#6e7289,#6a6e84
#676a80,#63677d,#676a80,#595c71
#585b70,#585b70,#585b70,#585b70
#434558,#38394b,#353648
#2e2f41,#2c2d3e,#252536,#252536
#9b56ef,#a86cf1,#290850
1 #f9e2af #f8c459 #bc8009 #664506
2 #a6e3a1 #6cd163 #359b2e #1c5017
3 #fab387 #f77e33 #b44908 #6e2d04
4 #89b4fa #307bf7 #0846b3 #052459
5 #cba6f7 #8a39ec #5710ad #290850
6 #f5c2e7 #e86ec6 #b11b87 #490b38
7 #f2cdcd #d76363 #912424 #4c1313
8 #94e2d5 #54d1bc #258e7e #103b35
9 #cdd6f4 #7f849c #6c7086 #585b70
10 #45475a #313244 #1e1e2e #11111b
11 #f9d791 #f9d487 #f8cf78 #f8ca69
12 #f9dea3 #f8ca69 #f8ca69 #f8ca69
13 #f8d07c #f8d07c #f8ca69 #f8ca69
14 #c28711 #ad7608 #b07809 #ebb547
15 #f9d487 #f8d17e #d29926 #664506
16 #664506
17 #f7863f #f7863f #c35512
18 #e9aaaa #e9aaaa #f2cdcd #f2cdcd
19 #df8383 #da6e6e #cb5858 #9a2c2c
20 #581616 #4f1414 #4f1414
21 #89da82 #89da82 #89da82 #86d97f
22 #61c658 #47ad40 #a6e3a1
23 #308c29 #318e2a #1c5017 #1c5017
24 #89b4fa #89b4fa #89b4fa #89b4fa
25 #89b4fa #89b4fa #89b4fa #89b4fa
26 #7cacfa #89b4fa #659df9 #679ef9
27 #78a9f9 #4f8ff8 #6aa0f9
28 #5190f8 #5190f8 #5190f8 #5190f8
29 #5190f8 #5190f8 #5190f8 #5190f8
30 #5693f8 #86b2fa #3c83f7 #3b82f7
31 #347ef7 #266ee6 #2168de
32 #0845b0 #0841a6
33 #05255a #052966
34 #74dac8 #49c1ad #258d7d #103b35
35 #b7bfdc #afb6d2 #aab2cd #b7bfdc
36 #8c92ab #8e94ad #8389a1 #7f849c
37 #7b8098 #797e95 #777c93
38 #73778e #70748a #6e7289 #6a6e84
39 #676a80 #63677d #676a80 #595c71
40 #585b70 #585b70 #585b70 #585b70
41 #434558 #38394b #353648
42 #2e2f41 #2c2d3e #252536 #252536
43 #9b56ef #a86cf1 #290850

209
icons/retheme.py Normal file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/env python3
"""Retheme FreeCAD SVG icons by replacing color palettes.
Reads two CSV files with matching rows (source and target palettes),
builds a color mapping, and applies it to all SVG files found in the
input directories.
Usage:
python icons/retheme.py [options]
python icons/retheme.py --dry-run
"""
import argparse
import csv
import re
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
REPO_ROOT = SCRIPT_DIR.parent
DEFAULT_SOURCE = SCRIPT_DIR / "mappings" / "FCAD.csv"
DEFAULT_TARGET = SCRIPT_DIR / "mappings" / "kindred.csv"
DEFAULT_OUTPUT = SCRIPT_DIR / "themed"
# Default input dirs: core GUI icons + all module icons
DEFAULT_INPUT_DIRS = [
REPO_ROOT / "src" / "Gui" / "Icons",
*sorted(REPO_ROOT.glob("src/Mod/*/Gui/Resources/icons")),
*sorted(REPO_ROOT.glob("src/Mod/*/Resources/icons")),
]
def load_palette(path: Path) -> list[list[str]]:
"""Load a palette CSV. Each row is a list of hex color strings."""
rows = []
with open(path) as f:
reader = csv.reader(f)
for row in reader:
colors = []
for cell in row:
cell = cell.strip()
if cell:
if not cell.startswith("#"):
cell = "#" + cell
colors.append(cell.lower())
if colors:
rows.append(colors)
return rows
def build_mapping(source_path: Path, target_path: Path) -> dict[str, str]:
"""Build a {source_hex: target_hex} dict from two palette CSVs."""
source = load_palette(source_path)
target = load_palette(target_path)
if len(source) != len(target):
print(
f"Error: palette row count mismatch: "
f"{source_path.name} has {len(source)} rows, "
f"{target_path.name} has {len(target)} rows",
file=sys.stderr,
)
sys.exit(1)
mapping = {}
for i, (src_row, tgt_row) in enumerate(zip(source, target)):
if len(src_row) != len(tgt_row):
print(
f"Error: column count mismatch on row {i + 1}: "
f"{len(src_row)} source colors vs {len(tgt_row)} target colors",
file=sys.stderr,
)
sys.exit(1)
for src, tgt in zip(src_row, tgt_row):
mapping[src] = tgt
return mapping
def retheme_svg(content: str, mapping: dict[str, str]) -> tuple[str, dict[str, int]]:
"""Replace all mapped hex colors in SVG content.
Returns the modified content and a dict of {color: replacement_count}.
"""
stats: dict[str, int] = {}
def replacer(match: re.Match) -> str:
color = match.group(0).lower()
target = mapping.get(color)
if target is not None:
stats[color] = stats.get(color, 0) + 1
# Preserve original case style (all-lower for consistency)
return target
return match.group(0)
result = re.sub(r"#[0-9a-fA-F]{6}\b", replacer, content)
return result, stats
def collect_svgs(input_dirs: list[Path]) -> list[Path]:
"""Collect all .svg files from input directories, recursively."""
svgs = []
for d in input_dirs:
if d.is_dir():
svgs.extend(sorted(d.rglob("*.svg")))
return svgs
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--source-palette",
type=Path,
default=DEFAULT_SOURCE,
help=f"Source palette CSV (default: {DEFAULT_SOURCE.relative_to(REPO_ROOT)})",
)
parser.add_argument(
"--target-palette",
type=Path,
default=DEFAULT_TARGET,
help=f"Target palette CSV (default: {DEFAULT_TARGET.relative_to(REPO_ROOT)})",
)
parser.add_argument(
"--input-dir",
type=Path,
action="append",
dest="input_dirs",
help="Input directory containing SVGs (can be repeated; default: FreeCAD icon dirs)",
)
parser.add_argument(
"--output-dir",
type=Path,
default=DEFAULT_OUTPUT,
help=f"Output directory for themed SVGs (default: {DEFAULT_OUTPUT.relative_to(REPO_ROOT)})",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be done without writing files",
)
args = parser.parse_args()
input_dirs = args.input_dirs or DEFAULT_INPUT_DIRS
# Build color mapping
mapping = build_mapping(args.source_palette, args.target_palette)
print(f"Loaded {len(mapping)} color mappings")
for src, tgt in mapping.items():
print(f" {src} -> {tgt}")
# Collect SVGs
svgs = collect_svgs(input_dirs)
print(f"\nFound {len(svgs)} SVG files across {len(input_dirs)} directories")
if not svgs:
print("No SVGs found. Check --input-dir paths.", file=sys.stderr)
sys.exit(1)
# Process
if not args.dry_run:
args.output_dir.mkdir(parents=True, exist_ok=True)
total_files = 0
total_replacements = 0
all_unmapped: dict[str, int] = {}
seen_names: set[str] = set()
for svg_path in svgs:
# Skip duplicates (same filename from different subdirectories)
if svg_path.name in seen_names:
continue
seen_names.add(svg_path.name)
content = svg_path.read_text(encoding="utf-8")
themed, stats = retheme_svg(content, mapping)
total_files += 1
file_replacements = sum(stats.values())
total_replacements += file_replacements
if not args.dry_run:
out_path = args.output_dir / svg_path.name
out_path.write_text(themed, encoding="utf-8")
# Track unmapped colors in this file
for match in re.finditer(r"#[0-9a-fA-F]{6}\b", content):
color = match.group(0).lower()
if color not in mapping:
all_unmapped[color] = all_unmapped.get(color, 0) + 1
# Summary
action = "Would write" if args.dry_run else "Wrote"
print(
f"\n{action} {total_files} themed SVGs with {total_replacements} color replacements"
)
print(f" Output: {args.output_dir}")
if all_unmapped:
print(f"\nUnmapped colors ({len(all_unmapped)} unique):")
for color, count in sorted(all_unmapped.items(), key=lambda x: -x[1])[:20]:
print(f" {color} ({count} occurrences)")
if len(all_unmapped) > 20:
print(f" ... and {len(all_unmapped) - 20} more")
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="64px" height="64px" id="svg2985" version="1.1" inkscape:version="0.48.5 r10040" sodipodi:docname="Arch_3Views.svg">
<defs id="defs2987">
<inkscape:perspective sodipodi:type="inkscape:persp3d" inkscape:vp_x="0 : 32 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="64 : 32 : 1" inkscape:persp3d-origin="32 : 21.333333 : 1" id="perspective2993"/>
</defs>
<sodipodi:namedview id="base" pagecolor="#cdd6f4" bordercolor="#38394b" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.9062855" inkscape:cx="55.690563" inkscape:cy="28.554415" inkscape:current-layer="g5297" showgrid="true" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:window-width="800" inkscape:window-height="837" inkscape:window-x="0" inkscape:window-y="27" inkscape:window-maximized="0" inkscape:snap-global="true">
<inkscape:grid type="xygrid" id="grid2992" empspacing="2" visible="true" enabled="true" snapvisiblegridlinesonly="true"/>
</sodipodi:namedview>
<metadata id="metadata2990">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_3Views</dc:title>
<dc:date>2014-08-29</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_3Views.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer">
<g id="g5297" transform="translate(10.542319,6.1711137)">
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path3009-7-2" d="m 18.457681,20.828886 -14,-8 0,34 14,8 z" style="color:#11111b;fill:#6cd163;fill-opacity:1;fill-rule:nonzero;stroke:#1c5017;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path3009-6-0-8" d="m 34.457681,12.828886 -16,8 0,34 16,-8 z" style="color:#11111b;fill:#359b2e;fill-opacity:1;fill-rule:nonzero;stroke:#1c5017;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path3029-8-6" d="m 4.457681,12.828886 16,-7.9999997 14,7.9999997 -16,8 z" style="color:#11111b;fill:#a6e3a1;fill-opacity:1;fill-rule:nonzero;stroke:#1c5017;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
<path style="fill:none;stroke:#a6e3a1;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 6.475023,16.273948 -0.034684,29.38152 10.017342,5.757214 0,-29.410378 z" id="path2994" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
<path inkscape:connector-curvature="0" id="path3055" d="m 4.457681,12.828886 14,42" style="color:#11111b;fill:#89da82;fill-opacity:1;fill-rule:nonzero;stroke:#1c5017;stroke-width:1.85461509;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" sodipodi:nodetypes="cc"/>
<path style="fill:none;stroke:#6cd163;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 20.44752,22.035362 0,29.567809 12.052567,-6.000271 -0.01734,-29.550467 z" id="path2996" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
<path inkscape:connector-curvature="0" id="path3057" d="m 18.457681,54.828886 16,-42 -28.741113,-0.0445" style="color:#11111b;fill:none;stroke:#1c5017;stroke-width:1.85461509;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" sodipodi:nodetypes="ccc"/>
<g id="g3813" transform="translate(2,0)">
<path style="color:#11111b;fill:#0846b3;fill-opacity:1;fill-rule:nonzero;stroke:#052459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 42.485658,22.702462 -16,8 -0.02798,24.126424 16,0 z" id="path3009-6-0-8-3" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path2996-1" d="m 28.475497,31.908938 -0.01782,20.919948 12,0 0.05304,-26.902876 z" style="fill:none;stroke:#307bf7;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
</g>
<g id="g3856" transform="translate(2,0)">
<path style="color:#11111b;fill:#307bf7;fill-opacity:1;fill-rule:nonzero;stroke:#052459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" d="m 8.457681,30.828886 -14,-8 0,32 14,0 z" id="path3009-7-2-2" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path2994-7" d="m -3.524977,26.273949 -0.017342,26.554937 10,0 0,-20.826582 z" style="fill:#307bf7;stroke:#89b4fa;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"/>
</g>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path3029-8-6-7" d="m 4.457681,4.828886 16,-7.9999997 14,7.9999997 -16,8 z" style="color:#11111b;fill:#89b4fa;fill-opacity:1;fill-rule:nonzero;stroke:#052459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

71
icons/themed/Arch_Add.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

335
icons/themed/Arch_Axis.svg Normal file
View File

@@ -0,0 +1,335 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Axis.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3896"
inkscape:collect="always">
<stop
id="stop3898"
offset="0"
style="stop-color:#bc8009;stop-opacity:1" />
<stop
id="stop3900"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3877">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3879" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3881" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3812">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3814" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3816" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3804">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3806" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3808" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3796">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3798" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3800" />
</linearGradient>
<linearGradient
id="linearGradient3883">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887" />
</linearGradient>
<linearGradient
id="linearGradient3793">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3793-2"
id="linearGradient3799-8"
x1="12.037806"
y1="54.001419"
x2="52.882648"
y2="9.274148"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3793-2">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795-6" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3883-6"
id="linearGradient3889-4"
x1="3"
y1="31.671875"
x2="59.25"
y2="31.671875"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-1.2727273,-0.18181818)" />
<linearGradient
id="linearGradient3883-6">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885-4" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887-5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3796"
id="linearGradient3802"
x1="16.4375"
y1="59.705883"
x2="8.5625"
y2="40.294117"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3804"
id="linearGradient3810"
x1="16.4375"
y1="58.411766"
x2="8.5625"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3812"
id="linearGradient3818"
x1="16.4375"
y1="58.411766"
x2="8.562501"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3886"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.8,0,0,0.8,-11.6,-54.6)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3877"
id="linearGradient3888"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.8,0,0,0.8,-11.6,-28.6)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.7232161"
inkscape:cx="73.255013"
inkscape:cy="31.608623"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid2999"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Axis</dc:title>
<dc:date>2011-12-12</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Axis.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040"
width="40"
height="6"
x="22"
y="-48"
transform="rotate(90)" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3042"
width="40"
height="6"
x="22"
y="-22"
transform="rotate(90)" />
<circle
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6"
cx="14"
cy="-19"
r="10"
transform="rotate(90)" />
<circle
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3"
cx="14"
cy="-45"
r="10"
transform="rotate(90)" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3040-5"
width="39"
height="4"
x="22"
y="-47"
transform="rotate(90)" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3042-3"
width="39"
height="4"
x="22"
y="-21"
transform="rotate(90)" />
<circle
style="fill:url(#linearGradient3888);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-6"
cx="14"
cy="-19"
r="8"
transform="rotate(90)" />
<circle
style="fill:url(#linearGradient3886);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-2"
cx="14"
cy="-45"
r="8"
transform="rotate(90)" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 18,22 V 61"
id="path3910"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 44,22 V 61"
id="path3910-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,426 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Axis_System.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3896"
inkscape:collect="always">
<stop
id="stop3898"
offset="0"
style="stop-color:#bc8009;stop-opacity:1" />
<stop
id="stop3900"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3812">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3814" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3816" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3804">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3806" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3808" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3796">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3798" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3800" />
</linearGradient>
<linearGradient
id="linearGradient3883">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887" />
</linearGradient>
<linearGradient
id="linearGradient3793">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3793-2"
id="linearGradient3799-8"
x1="12.037806"
y1="54.001419"
x2="52.882648"
y2="9.274148"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3793-2">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795-6" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3883-6"
id="linearGradient3889-4"
x1="3"
y1="31.671875"
x2="59.25"
y2="31.671875"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-1.2727273,-0.18181818)" />
<linearGradient
id="linearGradient3883-6">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885-4" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887-5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3796"
id="linearGradient3802"
x1="16.4375"
y1="59.705883"
x2="8.5625"
y2="40.294117"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3804"
id="linearGradient3810"
x1="16.4375"
y1="58.411766"
x2="8.5625"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3812"
id="linearGradient3818"
x1="16.4375"
y1="58.411766"
x2="8.562501"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,-12.6,18.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3886-0"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,-12.6,40.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3888-6"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,4.4,3.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3884-2"
x1="34.5"
y1="20.75"
x2="27"
y2="3.25"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,26.4,3.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3884-2-4"
x1="34.5"
y1="20.75"
x2="27"
y2="3.25"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.6796362"
inkscape:cx="33.405573"
inkscape:cy="42.101045"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid2999"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Axis</dc:title>
<dc:date>2011-12-12</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Axis.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3044-8"
width="6"
height="40"
x="27"
y="21" />
<circle
r="10"
cy="13"
cx="30"
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-7" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040-9"
width="40"
height="6"
x="21"
y="25" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3042-2"
width="40"
height="6"
x="21"
y="47" />
<circle
r="10"
cy="50"
cx="13"
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-0" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3044-7-2"
width="4"
height="39"
x="28"
y="21" />
<circle
r="10"
cy="28"
cx="13"
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-3" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3044-8-5"
width="6"
height="40"
x="49"
y="21" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3040-5-7"
width="39"
height="4"
x="21"
y="26" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3042-3-5"
width="39"
height="4"
x="21"
y="48" />
<circle
r="8"
cy="13"
cx="30"
style="fill:url(#linearGradient3884-2);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-5-9" />
<circle
r="8"
cy="50"
cx="13"
style="fill:url(#linearGradient3888-6);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-6-2" />
<circle
r="8"
cy="28"
cx="13"
style="fill:url(#linearGradient3886-0);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-2-2" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 21,27 h 8 v -6"
id="path3902-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
d="m 21,49 h 8 V 31"
id="path3904-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 29,60 V 52"
id="path3906-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,49 H 60"
id="path3910-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<circle
r="10"
cy="13"
cx="52"
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-7-7" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3044-7-2-4"
width="4"
height="39"
x="50"
y="21" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,27 H 50"
id="path3908-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<circle
r="8"
cy="13"
cx="52"
style="fill:url(#linearGradient3884-2-4);fill-opacity:1;stroke:#f9e2af;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-5-9-9" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,60 V 52"
id="path3906-7-9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,50 V 30"
id="path3906-7-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,28 V 20"
id="path3906-7-9-9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54,27 h 6"
id="path3910-6-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,421 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Axis_System_Tree.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3896"
inkscape:collect="always">
<stop
id="stop3898"
offset="0"
style="stop-color:#6c7086;stop-opacity:1" />
<stop
id="stop3900"
offset="1"
style="stop-color:#cdd6f4;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3812">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3814" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3816" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3804">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3806" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3808" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3796">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3798" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3800" />
</linearGradient>
<linearGradient
id="linearGradient3883">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887" />
</linearGradient>
<linearGradient
id="linearGradient3793">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3793-2"
id="linearGradient3799-8"
x1="12.037806"
y1="54.001419"
x2="52.882648"
y2="9.274148"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3793-2">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795-6" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3883-6"
id="linearGradient3889-4"
x1="3"
y1="31.671875"
x2="59.25"
y2="31.671875"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-1.2727273,-0.18181818)" />
<linearGradient
id="linearGradient3883-6">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885-4" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887-5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3796"
id="linearGradient3802"
x1="16.4375"
y1="59.705883"
x2="8.5625"
y2="40.294117"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3804"
id="linearGradient3810"
x1="16.4375"
y1="58.411766"
x2="8.5625"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3812"
id="linearGradient3818"
x1="16.4375"
y1="58.411766"
x2="8.562501"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,-12.6,18.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3886-1"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,-12.6,40.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3888-8"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,4.4,3.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3884-1"
x1="34.5"
y1="20.75"
x2="27"
y2="3.25"
gradientUnits="userSpaceOnUse" />
<linearGradient
gradientTransform="matrix(0.8,0,0,0.8,26.4,3.4)"
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3884-1-4"
x1="34.5"
y1="20.75"
x2="27"
y2="3.25"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.189996"
inkscape:cx="12.900184"
inkscape:cy="29.6413"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid2999"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Axis_Tree</dc:title>
<dc:date>2011-12-12</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Axis_Tree.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3044-0"
width="6"
height="40"
x="27"
y="21" />
<circle
r="10"
cy="13"
cx="30"
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-4" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040-4"
width="40"
height="6"
x="21"
y="25" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3044-0-0"
width="6"
height="40"
x="49"
y="21" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3042-4"
width="40"
height="6"
x="21"
y="47" />
<circle
r="10"
cy="50"
cx="13"
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-4" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3044-7-7"
width="4"
height="39"
x="28"
y="21" />
<circle
r="10"
cy="28"
cx="13"
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-6" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3040-5-3"
width="39"
height="4"
x="21"
y="26" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3042-3-1"
width="39"
height="4"
x="21"
y="48" />
<circle
r="8"
cy="13"
cx="30"
style="fill:url(#linearGradient3884-1);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-5-7" />
<circle
r="8"
cy="50"
cx="13"
style="fill:url(#linearGradient3888-8);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-6-5" />
<circle
r="8"
cy="28"
cx="13"
style="fill:url(#linearGradient3886-1);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-2-9" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 21,27 h 8 v -6"
id="path3902-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
d="m 21,49 h 8 V 31"
id="path3904-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 29,60 V 52"
id="path3906-1"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 36,27 H 60"
id="path3908-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,49 H 60"
id="path3910-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<circle
r="10"
cy="13"
cx="52"
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-4-8" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3044-7-7-6"
width="4"
height="39"
x="50"
y="21" />
<circle
r="8"
cy="13"
cx="52"
style="fill:url(#linearGradient3884-1-4);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-5-7-2" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,27 H 51 V 21"
id="path3902-6-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,60 V 52"
id="path3906-1-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,50 V 30"
id="path3906-1-7-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,335 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Axis_Tree.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3896"
inkscape:collect="always">
<stop
id="stop3898"
offset="0"
style="stop-color:#6c7086;stop-opacity:1" />
<stop
id="stop3900"
offset="1"
style="stop-color:#cdd6f4;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3877">
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="0"
id="stop3879" />
<stop
style="stop-color:#cdd6f4;stop-opacity:1"
offset="1"
id="stop3881" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3812">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3814" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3816" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3804">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3806" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3808" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3796">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3798" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3800" />
</linearGradient>
<linearGradient
id="linearGradient3883">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887" />
</linearGradient>
<linearGradient
id="linearGradient3793">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3793-2"
id="linearGradient3799-8"
x1="12.037806"
y1="54.001419"
x2="52.882648"
y2="9.274148"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3793-2">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795-6" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3883-6"
id="linearGradient3889-4"
x1="3"
y1="31.671875"
x2="59.25"
y2="31.671875"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-1.2727273,-0.18181818)" />
<linearGradient
id="linearGradient3883-6">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885-4" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887-5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3796"
id="linearGradient3802"
x1="16.4375"
y1="59.705883"
x2="8.5625"
y2="40.294117"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3804"
id="linearGradient3810"
x1="16.4375"
y1="58.411766"
x2="8.5625"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3812"
id="linearGradient3818"
x1="16.4375"
y1="58.411766"
x2="8.562501"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3896"
id="linearGradient3886"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.8,0,0,0.8,-11.6,-54.6)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3877"
id="linearGradient3888"
x1="35.75"
y1="19.5"
x2="28.25"
y2="4.5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.8,0,0,0.8,-11.6,-28.6)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.3769881"
inkscape:cx="47.483737"
inkscape:cy="15.934805"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid2999"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Axis_Tree</dc:title>
<dc:date>2011-12-12</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Axis_Tree.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040"
width="40"
height="6"
x="22"
y="-48"
transform="rotate(90)" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3042"
width="40"
height="6"
x="22"
y="-22"
transform="rotate(90)" />
<circle
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6"
cx="14"
cy="-19"
r="10"
transform="rotate(90)" />
<circle
style="fill:#6c7086;fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3"
cx="14"
cy="-45"
r="10"
transform="rotate(90)" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3040-5"
width="39"
height="4"
x="22"
y="-47"
transform="rotate(90)" />
<rect
style="fill:#6c7086;fill-opacity:1;stroke:none"
id="rect3042-3"
width="39"
height="4"
x="22"
y="-21"
transform="rotate(90)" />
<circle
style="fill:url(#linearGradient3888);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-6-6"
cx="14"
cy="-19"
r="8"
transform="rotate(90)" />
<circle
style="fill:url(#linearGradient3886);fill-opacity:1;stroke:#cdd6f4;stroke-width:2.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="path3013-3-2"
cx="14"
cy="-45"
r="8"
transform="rotate(90)" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 44,61 V 22"
id="path3906-1-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 18,61 V 22"
id="path3906-1-9-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,454 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48.000000px"
height="48.000000px"
id="svg249"
sodipodi:version="0.32"
inkscape:version="0.48.5 r10040"
sodipodi:docname="drawing-draft-view.svg"
inkscape:export-filename="/home/jimmac/gfx/novell/pdes/trunk/docs/BIGmime-text.png"
inkscape:export-xdpi="240.00000"
inkscape:export-ydpi="240.00000"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs3">
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5031"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" />
<linearGradient
inkscape:collect="always"
id="linearGradient5060">
<stop
style="stop-color:black;stop-opacity:1;"
offset="0"
id="stop5062" />
<stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5064" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient5060"
id="radialGradient5029"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)"
cx="605.71429"
cy="486.64789"
fx="605.71429"
fy="486.64789"
r="117.14286" />
<linearGradient
id="linearGradient5048">
<stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" />
<stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" />
<stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5048"
id="linearGradient5027"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)"
x1="302.85715"
y1="366.64789"
x2="302.85715"
y2="609.50507" />
<linearGradient
inkscape:collect="always"
id="linearGradient4542">
<stop
style="stop-color:#11111b;stop-opacity:1;"
offset="0"
id="stop4544" />
<stop
style="stop-color:#11111b;stop-opacity:0;"
offset="1"
id="stop4546" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4542"
id="radialGradient4548"
cx="24.306795"
cy="42.07798"
fx="24.306795"
fy="42.07798"
r="15.821514"
gradientTransform="matrix(1.000000,0.000000,0.000000,0.284916,-6.310056e-16,30.08928)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient15662">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop15664" />
<stop
style="stop-color:#afb6d2;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop15666" />
</linearGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="64.5679"
fx="20.8921"
r="5.257"
cy="64.5679"
cx="20.8921"
id="aigrd3">
<stop
id="stop15573"
style="stop-color:#8c92ab"
offset="0" />
<stop
id="stop15575"
style="stop-color:#585b70;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="114.5684"
fx="20.8921"
r="5.256"
cy="114.5684"
cx="20.8921"
id="aigrd2">
<stop
id="stop15566"
style="stop-color:#8c92ab"
offset="0" />
<stop
id="stop15568"
style="stop-color:#585b70;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<linearGradient
id="linearGradient269">
<stop
style="stop-color:#585b70;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop270" />
<stop
style="stop-color:#2c2d3e;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop271" />
</linearGradient>
<linearGradient
id="linearGradient259">
<stop
style="stop-color:#b7bfdc;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop260" />
<stop
style="stop-color:#595c71;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop261" />
</linearGradient>
<linearGradient
id="linearGradient12512">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop12513" />
<stop
style="stop-color:#f9d487;stop-opacity:0.89108908;"
offset="0.50000000"
id="stop12517" />
<stop
style="stop-color:#f8ca69;stop-opacity:0.0000000;"
offset="1.0000000"
id="stop12514" />
</linearGradient>
<radialGradient
r="37.751713"
fy="3.7561285"
fx="8.8244190"
cy="3.7561285"
cx="8.8244190"
gradientTransform="matrix(0.96827297,0,0,1.032767,29.045513,-115.18343)"
gradientUnits="userSpaceOnUse"
id="radialGradient15656"
xlink:href="#linearGradient269"
inkscape:collect="always" />
<radialGradient
r="86.708450"
fy="35.736916"
fx="33.966679"
cy="35.736916"
cx="33.966679"
gradientTransform="matrix(0.96049297,0,0,1.041132,25.691961,-115.82988)"
gradientUnits="userSpaceOnUse"
id="radialGradient15658"
xlink:href="#linearGradient259"
inkscape:collect="always" />
<radialGradient
r="38.158695"
fy="7.2678967"
fx="8.1435566"
cy="7.2678967"
cx="8.1435566"
gradientTransform="matrix(0.96827297,0,0,1.032767,29.045513,-115.18343)"
gradientUnits="userSpaceOnUse"
id="radialGradient15668"
xlink:href="#linearGradient15662"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#aigrd2"
id="radialGradient2283"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
cx="20.8921"
cy="114.5684"
fx="20.8921"
fy="114.5684"
r="5.256" />
<radialGradient
inkscape:collect="always"
xlink:href="#aigrd3"
id="radialGradient2285"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.229703,0,0,0.229703,4.613529,3.979808)"
cx="20.8921"
cy="64.5679"
fx="20.8921"
fy="64.5679"
r="5.257" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-76"
id="linearGradient4343"
gradientUnits="userSpaceOnUse"
x1="18.971846"
y1="14.452502"
x2="44.524982"
y2="41.792759" />
<linearGradient
id="linearGradient3377-76">
<stop
style="stop-color:#f9d791;stop-opacity:1;"
offset="0"
id="stop3379-5" />
<stop
id="stop4345"
offset="0.5"
style="stop-color:#fcb915;stop-opacity:1;" />
<stop
style="stop-color:#c68708;stop-opacity:1;"
offset="1"
id="stop3381-7" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3377-76"
id="linearGradient4349"
x1="145.64697"
y1="79.160103"
x2="175.6825"
y2="108.75008"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient4482">
<stop
style="stop-color:#f9d791;stop-opacity:1;"
offset="0"
id="stop4484" />
<stop
id="stop4486"
offset="0.5"
style="stop-color:#fcb915;stop-opacity:1;" />
<stop
style="stop-color:#c68708;stop-opacity:1;"
offset="1"
id="stop4488" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient4351"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.97435,0.2250379,-0.4623105,2.0016728,48.487554,-127.99883)"
cx="135.38333"
cy="97.369568"
fx="135.38333"
fy="97.369568"
r="19.467436" />
<linearGradient
id="linearGradient3377">
<stop
style="stop-color:#f9d791;stop-opacity:1;"
offset="0"
id="stop3379" />
<stop
style="stop-color:#f8ca69;stop-opacity:1;"
offset="1"
id="stop3381" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3377"
id="radialGradient4353"
gradientUnits="userSpaceOnUse"
cx="45.883327"
cy="28.869568"
fx="45.883327"
fy="28.869568"
r="19.467436" />
<linearGradient
id="linearGradient4495">
<stop
style="stop-color:#f9d791;stop-opacity:1;"
offset="0"
id="stop4497" />
<stop
style="stop-color:#f8ca69;stop-opacity:1;"
offset="1"
id="stop4499" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="0.32941176"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.01908"
inkscape:cx="33.4692"
inkscape:cy="19.044121"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1053"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:showpageshadow="false"
inkscape:window-maximized="1"
inkscape:object-nodes="true" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Jakub Steiner</dc:title>
</cc:Agent>
</dc:creator>
<dc:source>http://jimmac.musichall.cz</dc:source>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Attribution" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Shadow"
id="layer6"
inkscape:groupmode="layer" />
<g
id="layer1"
inkscape:label="Base"
inkscape:groupmode="layer"
style="display:inline">
<path
style="fill:#696969;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;color:#11111b;fill-opacity:1;fill-rule:nonzero;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 25.319719,16.100786 c 0,0 -7.033257,0.862141 -7.033257,0.862141 l 0.64402,21.218891 6.481285,-2.117647 z"
id="path3092"
inkscape:connector-curvature="0" />
<path
style="fill:#2d5b89;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 6.4433689,15.964659 11.0263281,5.490476 c 0,0 0.998269,25.047462 0.862141,24.775207 C 18.195711,45.958087 7.8953957,35.703148 7.8953957,35.703148 z"
id="path3082"
inkscape:connector-curvature="0" />
<path
style="fill:#535353;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;color:#11111b;fill-opacity:1;fill-rule:nonzero;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 25.319717,16.100786 0.09205,19.963385 7.031959,4.94795 0.862141,-22.23416 -7.986148,-2.677175 z"
id="path3090"
inkscape:connector-curvature="0" />
<path
style="fill:#23476b;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 6.4433689,15.964659 c 0,0 19.1032271,-2.495671 19.4208581,-2.450295 0.317631,0.04537 14.06651,3.993073 14.06651,3.993073 l -6.624872,1.270524 -7.986148,-2.677175 -7.033255,0.862141 7.396262,3.085557 -8.213027,1.406651 z"
id="path3084"
inkscape:connector-curvature="0" />
<path
style="fill:#3a74ae;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="m 17.469697,21.455135 8.213027,-1.406651 -0.136128,23.55006 -7.124006,2.654486 z"
id="path3086"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#3a74ae;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;color:#11111b;fill-opacity:1;fill-rule:nonzero;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 33.305865,18.777961 6.624872,-1.270524 -1.08902,21.099765 -6.397993,2.404919 z"
id="path3088"
inkscape:connector-curvature="0" />
<path
style="fill:#535353;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="M 17.87808,2.9871693 18.014207,9.9750483 25.7281,11.880833 25.68272,3.7131827 z"
id="path3094"
inkscape:connector-curvature="0" />
<path
style="fill:#414141;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="M 25.68272,3.7131827 33.804999,3.3955518 25.138214,2.7149143 17.87808,2.9871693 z"
id="path3096"
inkscape:connector-curvature="0" />
<path
style="fill:#696969;stroke:#11111b;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="M 25.68272,3.7131827 33.804999,3.3955518 33.532744,11.064068 25.7281,11.880833 z"
id="path3098"
inkscape:connector-curvature="0" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="new"
style="display:inline" />
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,494 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="Arch_BuildingPart.svg">
<defs
id="defs2818">
<linearGradient
id="linearGradient3071"
inkscape:collect="always">
<stop
id="stop3073"
offset="0"
style="stop-color:#bc8009;stop-opacity:1" />
<stop
id="stop3075"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient3633">
<stop
style="stop-color:#fff652;stop-opacity:1;"
offset="0"
id="stop3635" />
<stop
style="stop-color:#ffbf00;stop-opacity:1;"
offset="1"
id="stop3637" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3866"
id="linearGradient3872"
x1="35"
y1="53"
x2="24"
y2="9"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient3866">
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="0"
id="stop3868" />
<stop
style="stop-color:#cdd6f4;stop-opacity:1"
offset="1"
id="stop3870" />
</linearGradient>
<linearGradient
y2="9"
x2="24"
y1="64"
x1="34"
gradientUnits="userSpaceOnUse"
id="linearGradient3120"
xlink:href="#linearGradient3071"
inkscape:collect="always"
gradientTransform="translate(70,1)" />
<linearGradient
y2="9"
x2="24"
y1="64"
x1="34"
gradientUnits="userSpaceOnUse"
id="linearGradient3120-3"
xlink:href="#linearGradient3071"
inkscape:collect="always"
gradientTransform="translate(-2,-8)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3071"
id="linearGradient1065"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(70,1)"
x1="-44.80624"
y1="48.42857"
x2="-44.80624"
y2="12.523807" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="10.162079"
inkscape:cx="36.903644"
inkscape:cy="30.674644"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3032"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Floor</dc:title>
<dc:date>2011-10-10</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3067"
transform="matrix(1.4966666,0,0,1.6153846,-9.7066615,-19.846149)"
style="fill:url(#linearGradient3120);font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke:#664506;stroke-width:1.28626216;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none">
<path
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc"
inkscape:connector-curvature="0"
id="rect5347"
d="m 7.9109119,18.476189 v 4 22 h 2 2.0000001 l 13.28285,-10e-7 V 39.523807 H 13.167036 v -7.428572 h 9.35412 v -3.714286 h -9.35412 l -10e-7,-3.714285 h 4.00891 v -6.190476 z m 18.6191531,-1e-6 v 6.190476 h 5.345212 v 7.428571 h 4.008909 v -7.428571 h 5.345212 v 14.857143 h -5.126544 v -2.476191 h -4.227577 v 2.476191 l -1.336304,0 v 4.95238 l 1.918001,2e-6 h 3 8.453938 2 2 v -26 z"
style="color:#11111b;display:inline;overflow:visible;visibility:visible;vector-effect:none;fill:url(#linearGradient1065);fill-opacity:1;fill-rule:nonzero;stroke:#664506;stroke-width:1.28626215;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
</g>
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 15,12 H 4 v 38 h 23"
id="path1042"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 31,12 H 60 V 50 H 37"
id="path1044"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,462 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="Arch_BuildingPart_Tree.svg">
<defs
id="defs2818">
<linearGradient
id="linearGradient3071"
inkscape:collect="always">
<stop
id="stop3073"
offset="0"
style="stop-color:#bc8009;stop-opacity:1" />
<stop
id="stop3075"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient3633">
<stop
style="stop-color:#fff652;stop-opacity:1;"
offset="0"
id="stop3635" />
<stop
style="stop-color:#ffbf00;stop-opacity:1;"
offset="1"
id="stop3637" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3866"
id="linearGradient3872"
x1="35"
y1="53"
x2="24"
y2="9"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient3866">
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="0"
id="stop3868" />
<stop
style="stop-color:#cdd6f4;stop-opacity:1"
offset="1"
id="stop3870" />
</linearGradient>
<linearGradient
y2="9"
x2="24"
y1="64"
x1="34"
gradientUnits="userSpaceOnUse"
id="linearGradient3120-3"
xlink:href="#linearGradient3071"
inkscape:collect="always"
gradientTransform="translate(-2,-8)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="10.162079"
inkscape:cx="36.903644"
inkscape:cy="30.674644"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid3032"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Floor</dc:title>
<dc:date>2011-10-10</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3067"
transform="matrix(1.4966666,0,0,1.6153846,-9.7066615,-19.846149)"
style="fill:#cdd6f4;font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke:#22200e;stroke-width:1.70151215;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none">
<path
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc"
inkscape:connector-curvature="0"
id="rect5347"
d="m 7.9109119,18.476189 v 4 22 h 2 2.0000001 l 13.28285,-10e-7 V 39.523807 H 13.167036 v -7.428572 h 9.35412 v -3.714286 h -9.35412 l -10e-7,-3.714285 h 4.00891 v -6.190476 z m 18.6191531,-1e-6 v 6.190476 h 5.345212 v 7.428571 h 4.008909 v -7.428571 h 5.345212 v 14.857143 h -5.126544 v -2.476191 h -4.227577 v 2.476191 l -1.336304,0 v 4.95238 l 1.918001,2e-6 h 3 8.453938 2 2 v -26 z"
style="color:#11111b;display:inline;overflow:visible;visibility:visible;vector-effect:none;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:#22200e;stroke-width:1.70151215;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

130
icons/themed/Arch_Cell.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,580 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2980"
sodipodi:version="0.32"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="Arch_Component.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2982">
<linearGradient
inkscape:collect="always"
id="linearGradient3805">
<stop
style="stop-color:#359b2e;stop-opacity:1"
offset="0"
id="stop3807" />
<stop
style="stop-color:#a6e3a1;stop-opacity:1"
offset="1"
id="stop3809" />
</linearGradient>
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#89b4fa;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#0841a6;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3850"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.6028459,1.0471639,-1.9794021,1.1395295,127.9588,-74.456907)"
cx="51.328892"
cy="31.074146"
fx="51.328892"
fy="31.074146"
r="19.571428" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2988" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3805"
id="linearGradient3811"
x1="49.058823"
y1="60.823528"
x2="34.941177"
y2="23.17647"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient3838">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3840" />
<stop
style="stop-color:#f8c459;stop-opacity:1"
offset="1"
id="stop3842" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3828">
<stop
style="stop-color:#f8c459;stop-opacity:1"
offset="0"
id="stop3830" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3832" />
</linearGradient>
<linearGradient
id="linearGradient3633">
<stop
style="stop-color:#cdd6f4;stop-opacity:1;"
offset="0"
id="stop3635" />
<stop
style="stop-color:#ffbf00;stop-opacity:1;"
offset="1"
id="stop3637" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785-6"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785-67"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3848"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3869"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3894"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3828"
id="linearGradient1162"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-59.940632,-24.87265)"
x1="69.43573"
y1="81.495598"
x2="86.047386"
y2="43.697762" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3838"
id="linearGradient1189"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-81.017888,-10.36525)"
x1="130.59373"
y1="63.193493"
x2="129.11099"
y2="20.605619" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="8.1608652"
inkscape:cx="39.093046"
inkscape:cy="33.251441"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:snap-bbox="true"
inkscape:document-rotation="0">
<inkscape:grid
type="xygrid"
id="grid2991"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2985">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Component</dc:title>
<dc:date>2015-04-08</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Component.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:url(#linearGradient1189);fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stop-color:#11111b;stop-opacity:1"
d="M 36.358639,16.058671 36.236103,46.422014 12.008657,56.584475 12.236103,18.422014 Z"
id="path2995-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#f9e2af;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stop-color:#11111b;stop-opacity:1"
d="M 3,17 37,23 61,15 31,11 z"
id="path2993"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:url(#linearGradient1189);stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stop-color:#11111b;stop-opacity:1"
d="M 61,15 61,51 37,61 37,23 z"
id="path2995"
inkscape:connector-curvature="0" />
<path
id="path3825"
style="fill:url(#linearGradient1162);fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-variation-settings:normal;opacity:1;vector-effect:none;stop-color:#11111b;stop-opacity:1"
d="M 3 17 L 3 55 L 12 56.587891 L 12 30 L 26 32 L 26 59.058594 L 37 61 L 37 23 L 3 17 z " />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stop-color:#11111b;stop-opacity:1"
d="m 27.192817,57.216522 7.824524,1.399113 -0.0087,-33.933614 L 5,19.42772 5.00867,53.346836 10.27166,54.24942"
id="path3765"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#f8c459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stop-color:#11111b;stop-opacity:1"
d="m 39.01243,24.433833 -0.01226,33.535301 20.001105,-8.300993 3.6e-4,-31.867363 z"
id="path3775"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2980"
sodipodi:version="0.32"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="Arch_Component_Tree.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2982">
<linearGradient
inkscape:collect="always"
id="linearGradient3805">
<stop
style="stop-color:#359b2e;stop-opacity:1"
offset="0"
id="stop3807" />
<stop
style="stop-color:#a6e3a1;stop-opacity:1"
offset="1"
id="stop3809" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3777">
<stop
style="stop-color:#45475a;stop-opacity:1"
offset="0"
id="stop3779" />
<stop
style="stop-color:#585b70;stop-opacity:1"
offset="1"
id="stop3781" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3767">
<stop
style="stop-color:#585b70;stop-opacity:1"
offset="0"
id="stop3769" />
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="1"
id="stop3771" />
</linearGradient>
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#89b4fa;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#0841a6;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3850"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.6028459,1.0471639,-1.9794021,1.1395295,127.9588,-74.456907)"
cx="51.328892"
cy="31.074146"
fx="51.328892"
fy="31.074146"
r="19.571428" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2988" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3767"
id="linearGradient3773"
x1="22.116516"
y1="55.717518"
x2="17.328547"
y2="21.31134"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3777"
id="linearGradient3783"
x1="53.896763"
y1="51.179787"
x2="47.502235"
y2="21.83742"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3805"
id="linearGradient3811"
x1="49.058823"
y1="60.823528"
x2="34.941177"
y2="23.17647"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3777"
id="linearGradient895"
gradientUnits="userSpaceOnUse"
x1="53.896763"
y1="51.179787"
x2="47.502235"
y2="21.83742"
gradientTransform="translate(-24.763897,-4.5779861)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.541206"
inkscape:cx="30.574972"
inkscape:cy="32.8453"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
inkscape:snap-bbox="true"
inkscape:document-rotation="0">
<inkscape:grid
type="xygrid"
id="grid2991"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2985">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Component</dc:title>
<dc:date>2015-04-08</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Component.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:url(#linearGradient895);fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 36.358639,16.058671 36.236103,46.422014 12.008657,56.584475 12.236103,18.422014 Z"
id="path2995-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#cdd6f4;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 3,17 37,23 61,15 31,11 z"
id="path2993"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:url(#linearGradient3783);stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;fill-opacity:1"
d="M 61,15 61,51 37,61 37,23 z"
id="path2995"
inkscape:connector-curvature="0" />
<path
id="path3825"
style="fill:url(#linearGradient3773);fill-opacity:1;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M 3 17 L 3 55 L 12 56.587891 L 12 30 L 26 32 L 26 59.058594 L 37 61 L 37 23 L 3 17 z " />
<path
style="fill:none;stroke:#6c7086;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 27.192817,57.216522 7.824524,1.399113 -0.0087,-33.933614 L 5,19.42772 5.00867,53.346836 10.27166,54.24942"
id="path3765"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:none;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 39.01243,24.433833 -0.01226,33.535301 20.001105,-8.300993 3.6e-4,-31.867363 z"
id="path3775"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="Arch_CurtainWall.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#f8c459;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3794"
id="linearGradient3867"
gradientUnits="userSpaceOnUse"
x1="32.714748"
y1="27.398352"
x2="38.997726"
y2="3.6523125"
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,22.253893,65.739554)"
spreadMethod="reflect" />
<linearGradient
id="linearGradient3794-8">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-5" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-8" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886"
xlink:href="#linearGradient3794-8"
inkscape:collect="always" />
<linearGradient
id="linearGradient3794-1">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-2" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-2" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886-0"
xlink:href="#linearGradient3794-1"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3794"
id="linearGradient3867-3"
gradientUnits="userSpaceOnUse"
x1="32.714748"
y1="27.398352"
x2="38.997726"
y2="3.6523125"
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,20.172664,63.335227)"
spreadMethod="reflect" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.096831"
inkscape:cx="35.741065"
inkscape:cy="31.523595"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1360"
inkscape:window-height="739"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Site</dc:title>
<dc:date>2011-10-10</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:url(#linearGradient3867);fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712607 18.416343,9.7795791 3.081208,3.4042685 Z"
id="path3763"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 4.0627415,16.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
id="path890-5"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 5.2974093,30.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
id="path892-9"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 6.7565544,43.481706 21.277372,50.578904 38.047346,50.493083 54.4155,57.77384 56.62748,14.385588"
id="path3763-7-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:1;vector-effect:none;fill:url(#linearGradient3867);fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 18.639033,9.8487048 20.601037,52.227979"
id="path854"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:url(#linearGradient3867);fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
d="M 39.63247,8.4753024 39.436269,52.62038"
id="path856"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 16.639033,9.8487048 18.53167,49.696073"
id="path854-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#c9a138;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="M 37.63247,8.4753024 37.436269,52.62038"
id="path856-9"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 4.0627415,18.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
id="path890"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 5.2974093,32.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
id="path892"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#372c0f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712608 18.416343,9.7795792 3.081208,3.4042686 Z"
id="path3763-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="Arch_CurtainWall_Tree.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#f8c459;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
id="linearGradient3794-8">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-5" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-8" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886"
xlink:href="#linearGradient3794-8"
inkscape:collect="always" />
<linearGradient
id="linearGradient3794-1">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-2" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-2" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886-0"
xlink:href="#linearGradient3794-1"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3794"
id="linearGradient3867-3"
gradientUnits="userSpaceOnUse"
x1="32.714748"
y1="27.398352"
x2="38.997726"
y2="3.6523125"
gradientTransform="matrix(-0.36199711,-0.85553613,0.73810354,-0.31230866,20.172664,63.335227)"
spreadMethod="reflect" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.096831"
inkscape:cx="53.636136"
inkscape:cy="32.727474"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1360"
inkscape:window-height="739"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[wmayer]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Site</dc:title>
<dc:date>2011-10-10</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
style="fill:#cdd6f4;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712607 18.416343,9.7795791 3.081208,3.4042685 Z"
id="path3763"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 4.0627415,16.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
id="path890-5"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 5.2974093,30.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
id="path892-9"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="M 6.7565544,43.481706 21.277372,50.578904 38.047346,50.493083 54.4155,57.77384 56.62748,14.385588"
id="path3763-7-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="M 18.639033,9.8487048 20.601037,52.227979"
id="path854"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
d="M 39.63247,8.4753024 39.436269,52.62038"
id="path856"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="M 16.639033,9.8487048 18.53167,49.696073"
id="path854-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#585b70;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="M 37.63247,8.4753024 37.436269,52.62038"
id="path856-9"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 4.0627415,18.390392 14.7150265,6.474611 20.404836,-0.981002 18.639032,5.886011"
id="path890"
inkscape:connector-curvature="0" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;font-variant-east_asian:normal"
d="m 5.2974093,32.364138 14.5188257,6.670812 19.227634,-0.784802 17.26563,7.259413"
id="path892"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-opacity:1;stroke:#232323;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 6.6299713,45.573855 20.908004,52.532318 39.550895,52.411814 56.057784,60.629029 58.547233,14.153933 39.859557,8.7712608 18.416343,9.7795792 3.081208,3.4042686 Z"
id="path3763-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64px"
height="64px"
id="svg2860"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title1">Arch_CutPlane</title>
<defs
id="defs2862">
<linearGradient
id="linearGradient3">
<stop
style="stop-color:#89b4fa;stop-opacity:1;"
offset="0"
id="stop4" />
<stop
style="stop-color:#307bf7;stop-opacity:1;"
offset="1"
id="stop3" />
</linearGradient>
<linearGradient
id="linearGradient1">
<stop
style="stop-color:#307bf7;stop-opacity:1;"
offset="0"
id="stop2" />
<stop
style="stop-color:#0846b3;stop-opacity:1;"
offset="1"
id="stop1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient1"
id="linearGradient2"
x1="156"
y1="31.441406"
x2="164"
y2="56.558594"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-125,-7.0006774)" />
<linearGradient
xlink:href="#linearGradient3"
id="linearGradient4"
x1="126"
y1="27.361328"
x2="152"
y2="59.638672"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-125,-7.0006774)" />
</defs>
<metadata
id="metadata2865">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[wood galaxy]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_CutPlane</dc:title>
<dc:date>2014-11-11</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_CutPlane.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1">
<path
id="rect1-3-7-1"
style="fill:#56b4e9;stroke:#4c1313;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 47.00067,14.000078 57,15.666068" />
<path
id="rect1-3-2"
style="fill:none;stroke:#4c1313;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 47,40.999667 57,37.666333 V 15.666367 l -10.000667,3.333555" />
<path
id="rect1-3-7-1-9"
style="fill:#56b4e9;stroke:#89b4fa;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 47,14.000078 57,15.66689" />
<path
id="rect1-3-2-3"
style="fill:none;stroke:#89b4fa;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="M 47,40.999667 57,37.666333 V 15.666589 l -10,3.333333" />
<path
id="rect1-2"
style="fill:#d76363;fill-opacity:1;stroke:#4c1313;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 11,51.99968 36,6 V 12.000318 L 11,6.00032 v 10.999003 l 6,-2 24,4 v 26.000339 l -12,4 -18,-3.000001 z" />
<path
id="rect1-2-7"
style="fill:none;fill-opacity:1;stroke:#f2cdcd;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 13,46.332661 v 3.973659 l 32,5.332032 V 13.69368 L 13,8.3616479 v 7.9710081" />
<path
id="rect1"
style="fill:url(#linearGradient4);stroke:#11111b;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 5,18.999323 24,4 v 26.000339 l -24,-4 z" />
<path
id="rect1-3"
style="fill:url(#linearGradient2);stroke:#11111b;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 41,18.999323 -12,4 v 26.000339 l 12,-4 z" />
<path
id="rect1-3-7"
style="fill:#89b4fa;fill-opacity:1;stroke:#11111b;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="m 5,18.999323 12,-4 24,4 -12,4 z" />
<path
id="path1"
style="fill:none;stroke:#89b4fa;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 7,43.306303 20,3.332031 V 24.692682 L 7,21.360651 Z" />
<path
id="path1-0"
style="fill:none;stroke:#307bf7;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
d="m 31,24.440729 v 21.785495 l 8,-2.667968 V 21.772761 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

218
icons/themed/Arch_Fence.svg Normal file
View File

@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Fence.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#cdd6f4;stop-opacity:1"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
id="linearGradient3794-8">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-5" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-8" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886"
xlink:href="#linearGradient3794-8"
inkscape:collect="always" />
<linearGradient
id="linearGradient3794-1">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-2" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-2" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886-0"
xlink:href="#linearGradient3794-1"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.2080075"
inkscape:cx="27.389555"
inkscape:cy="21.184291"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="705"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Site_Tree</dc:title>
<dc:date>2011-12-06</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site_Tree.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g4538">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path931-5"
d="M 3,43 H 61 V 35 H 3 Z"
style="fill:#f8c459;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path933-8"
d="m 5,37 v 4 h 54 v -4 z"
style="fill:none;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4534">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path931"
d="M 3,57 H 61 V 49 H 3 Z"
style="fill:#f8c459;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path933"
d="m 5,51 v 4 h 54 v -4 z"
style="fill:none;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4518">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881"
d="M 5,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#f8c459;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883"
d="M 7,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4526">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881-4"
d="M 49,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#f8c459;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883-5"
d="M 51,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g4522">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881-4-4"
d="M 27,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#f8c459;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883-5-8"
d="M 29,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Fence_Tree.svg">
<defs
id="defs2987">
<linearGradient
id="linearGradient3794">
<stop
style="stop-color:#6c7086;stop-opacity:1"
offset="0"
id="stop3796" />
<stop
style="stop-color:#cdd6f4;stop-opacity:1"
offset="1"
id="stop3798" />
</linearGradient>
<linearGradient
id="linearGradient3794-8">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-5" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-8" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886"
xlink:href="#linearGradient3794-8"
inkscape:collect="always" />
<linearGradient
id="linearGradient3794-1">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3796-2" />
<stop
style="stop-color:#ffea00;stop-opacity:1;"
offset="1"
id="stop3798-2" />
</linearGradient>
<linearGradient
y2="23.848686"
x2="62.65237"
y1="23.848686"
x1="15.184971"
gradientTransform="matrix(1.0265568,0,0,0.91490626,-3.236706,-1.8027032)"
gradientUnits="userSpaceOnUse"
id="linearGradient3886-0"
xlink:href="#linearGradient3794-1"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.2080075"
inkscape:cx="50.905069"
inkscape:cy="22.363535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="705"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Site_Tree</dc:title>
<dc:date>2011-12-06</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Site_Tree.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
transform="translate(0,-12)"
id="g937-3">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path931-5"
d="M 3,55 H 61 V 47 H 3 Z"
style="fill:#585b70;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path933-8"
d="m 5,49 v 4 h 54 v -4 z"
style="fill:none;fill-rule:evenodd;stroke:#7f849c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g937"
transform="translate(0,2)">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path931"
d="M 3,55 H 61 V 47 H 3 Z"
style="fill:#585b70;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path933"
d="m 5,49 v 4 h 54 v -4 z"
style="fill:none;fill-rule:evenodd;stroke:#7f849c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
id="g887"
transform="translate(2)">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881"
d="M 3,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#585b70;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883"
d="M 5,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#7f849c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="translate(46)"
id="g887-4">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881-4"
d="M 3,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#585b70;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883-5"
d="M 5,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#7f849c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<g
transform="translate(24)"
id="g887-4-7">
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path881-4-4"
d="M 3,61 V 27 l 5,-6 5,6 v 34 z"
style="fill:#585b70;fill-rule:evenodd;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path883-5-8"
d="M 5,59 V 28 l 3,-4 3,4 v 31 z"
style="fill:none;fill-rule:evenodd;stroke:#7f849c;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

120
icons/themed/Arch_Floor.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

289
icons/themed/Arch_Frame.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 38 KiB

410
icons/themed/Arch_Grid.svg Normal file
View File

@@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
sodipodi:docname="Arch_Grid.svg">
<defs
id="defs2987">
<linearGradient
inkscape:collect="always"
id="linearGradient3812">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3814" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3816" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3804">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3806" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3808" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient3796">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3798" />
<stop
style="stop-color:#f8c459;stop-opacity:0;"
offset="1"
id="stop3800" />
</linearGradient>
<linearGradient
id="linearGradient3883">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887" />
</linearGradient>
<linearGradient
id="linearGradient3793">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3793-2"
id="linearGradient3799-8"
x1="12.037806"
y1="54.001419"
x2="52.882648"
y2="9.274148"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3793-2">
<stop
style="stop-color:#000f8a;stop-opacity:1;"
offset="0"
id="stop3795-6" />
<stop
style="stop-color:#5190f8;stop-opacity:1;"
offset="1"
id="stop3797-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3883-6"
id="linearGradient3889-4"
x1="3"
y1="31.671875"
x2="59.25"
y2="31.671875"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-1.2727273,-0.18181818)" />
<linearGradient
id="linearGradient3883-6">
<stop
style="stop-color:#ffb400;stop-opacity:1;"
offset="0"
id="stop3885-4" />
<stop
style="stop-color:#ffe900;stop-opacity:1;"
offset="1"
id="stop3887-5" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3796"
id="linearGradient3802"
x1="16.4375"
y1="59.705883"
x2="8.5625"
y2="40.294117"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3804"
id="linearGradient3810"
x1="16.4375"
y1="58.411766"
x2="8.5625"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3812"
id="linearGradient3818"
x1="16.4375"
y1="58.411766"
x2="8.562501"
y2="41.588234"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.6796362"
inkscape:cx="16.087193"
inkscape:cy="23.330282"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid2999"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[yorikvanhavre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Axis</dc:title>
<dc:date>2011-12-12</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Axis.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<rect
style="opacity:1;vector-effect:none;fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040-9-1"
width="57"
height="6"
x="4"
y="9" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none"
id="rect3044-8"
width="6"
height="56"
x="9"
y="5" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54,11 h 6"
id="path3910-6-3-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="opacity:1;vector-effect:none;fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3040-9"
width="57"
height="6"
x="4"
y="29" />
<rect
style="opacity:1;vector-effect:none;fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3042-2"
width="56"
height="6"
x="5"
y="49" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3044-7-2"
width="4"
height="39"
x="10"
y="21" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none"
id="rect3044-8-5"
width="6"
height="56"
x="49"
y="5" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3040-5-7-4"
width="39"
height="4"
x="21"
y="10" />
<rect
style="opacity:1;vector-effect:none;fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:1.92153788;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1"
id="rect3044-8-2-4"
width="6"
height="12"
x="29"
y="49" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3042-3-5"
width="39"
height="4"
x="21"
y="50" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
d="m 7,51 h 4 V 35"
id="path3904-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 11,60 V 52"
id="path3906-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 32,51 H 60"
id="path3910-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3044-7-2-4"
width="4"
height="39"
x="50"
y="21" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,60 V 52"
id="path3906-7-9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,52 V 32"
id="path3906-7-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54,31 h 6"
id="path3910-6-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:#664506;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0.60000002;stroke-opacity:1;font-variant-east_asian:normal;opacity:1;vector-effect:none"
id="rect3044-8-2"
width="6"
height="26"
x="29"
y="5" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3040-5-7-5"
width="39"
height="4"
x="5"
y="10" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5,11 H 50"
id="path3908-3-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 5,31 h 6 V 6"
id="path3902-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54,11 h 6"
id="path3910-6-9-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none;stroke-width:0.5547002"
id="rect3044-7-2-9"
width="4"
height="10"
x="30"
y="50" />
<rect
style="fill:#f8c459;fill-opacity:1;stroke:none"
id="rect3040-5-7"
width="39"
height="4"
x="21"
y="30" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 51,32 V 6"
id="path3906-7-9-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 34,31 H 50"
id="path3908-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 14,31 H 31 V 6"
id="path3902-8-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
d="M 15,51 H 33"
id="path3904-9-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 31,60 V 52"
id="path3906-7-8"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 54,31 h 6"
id="path3910-6-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="64px" height="64px" id="svg2816" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs id="defs2818">
<linearGradient id="linearGradient4044">
<stop style="stop-color:#11111b;stop-opacity:1" offset="0" id="stop4046" />
<stop style="stop-color:#11111b;stop-opacity:0" offset="1" id="stop4048" />
</linearGradient>
<linearGradient id="linearGradient3681">
<stop id="stop3697" offset="0" style="stop-color:#f8cf78;stop-opacity:1" />
<stop style="stop-color:#c35512;stop-opacity:1" offset="1" id="stop3685" />
</linearGradient>
<pattern patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)" id="pattern5231" xlink:href="#Strips1_1-4" />
<pattern id="Strips1_1-4" patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)" height="1" width="2" patternUnits="userSpaceOnUse">
<rect id="rect4483-4" height="2" width="1" y="-0.5" x="0" style="fill:black;stroke:none" />
</pattern>
<pattern patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)" id="pattern5231-4" xlink:href="#Strips1_1-6" />
<pattern id="Strips1_1-6" patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)" height="1" width="2" patternUnits="userSpaceOnUse">
<rect id="rect4483-0" height="2" width="1" y="-0.5" x="0" style="fill:black;stroke:none" />
</pattern>
<pattern patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)" id="pattern5296" xlink:href="#pattern5231-3" />
<pattern patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)" id="pattern5231-3" xlink:href="#Strips1_1-4-3" />
<pattern id="Strips1_1-4-3" patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)" height="1" width="2" patternUnits="userSpaceOnUse">
<rect id="rect4483-4-6" height="2" width="1" y="-0.5" x="0" style="fill:black;stroke:none" />
</pattern>
<pattern patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)" id="pattern5330" xlink:href="#Strips1_1-9" />
<pattern id="Strips1_1-9" patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)" height="1" width="2" patternUnits="userSpaceOnUse">
<rect id="rect4483-3" height="2" width="1" y="-0.5" x="0" style="fill:black;stroke:none" />
</pattern>
<linearGradient xlink:href="#linearGradient3681" id="linearGradient3687" x1="37.89756" y1="41.087898" x2="4.0605712" y2="40.168594" gradientUnits="userSpaceOnUse" />
<linearGradient xlink:href="#linearGradient3681" id="linearGradient3695" x1="31.777767" y1="40.24213" x2="68.442062" y2="54.041203" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.25023482,-0.66040068,0.68751357,0.24036653,-8.7488565,43.149938)" />
<radialGradient xlink:href="#linearGradient12512" id="radialGradient278" gradientUnits="userSpaceOnUse" cx="55" cy="125" fx="55" fy="125" r="14.375" />
<linearGradient id="linearGradient12512">
<stop style="stop-color:#cdd6f4;stop-opacity:1" offset="0.0000000" id="stop12513" />
<stop style="stop-color:#f9d487;stop-opacity:0.89108908" offset="0.50000000" id="stop12517" />
<stop style="stop-color:#f8ca69;stop-opacity:0" offset="1.0000000" id="stop12514" />
</linearGradient>
<radialGradient r="14.375" fy="125" fx="55" cy="125" cx="55" gradientUnits="userSpaceOnUse" id="radialGradient4017" xlink:href="#linearGradient12512" />
<radialGradient xlink:href="#linearGradient12512-2" id="radialGradient278-5" gradientUnits="userSpaceOnUse" cx="55" cy="125" fx="55" fy="125" r="14.375" />
<linearGradient id="linearGradient12512-2">
<stop style="stop-color:#cdd6f4;stop-opacity:1" offset="0.0000000" id="stop12513-3" />
<stop style="stop-color:#ffd820;stop-opacity:0.89108908" offset="0.5" id="stop12517-1" />
<stop style="stop-color:#ff8000;stop-opacity:0" offset="1" id="stop12514-6" />
</linearGradient>
<linearGradient xlink:href="#linearGradient4044" id="linearGradient3060" gradientUnits="userSpaceOnUse" x1="15.78776" y1="50.394047" x2="27.641447" y2="39.95837" />
<radialGradient xlink:href="#linearGradient12512-2" id="radialGradient3062" gradientUnits="userSpaceOnUse" cx="55" cy="125" fx="55" fy="125" r="14.375" />
<linearGradient xlink:href="#linearGradient4044-2" id="linearGradient3060-5" gradientUnits="userSpaceOnUse" x1="15.78776" y1="50.394047" x2="27.641447" y2="39.95837" />
<linearGradient id="linearGradient4044-2">
<stop style="stop-color:#11111b;stop-opacity:1" offset="0" id="stop4046-5" />
<stop style="stop-color:#11111b;stop-opacity:0" offset="1" id="stop4048-4" />
</linearGradient>
<radialGradient xlink:href="#linearGradient12512-2-7" id="radialGradient3062-5" gradientUnits="userSpaceOnUse" cx="55" cy="125" fx="55" fy="125" r="14.375" />
<linearGradient id="linearGradient12512-2-7">
<stop style="stop-color:#cdd6f4;stop-opacity:1" offset="0.0000000" id="stop12513-3-4" />
<stop style="stop-color:#ffd820;stop-opacity:0.89108908" offset="0.5" id="stop12517-1-9" />
<stop style="stop-color:#ff8000;stop-opacity:0" offset="1" id="stop12514-6-5" />
</linearGradient>
<radialGradient r="14.375" fy="125" fx="55" cy="125" cx="55" gradientUnits="userSpaceOnUse" id="radialGradient3086" xlink:href="#linearGradient12512-2-7" />
<linearGradient xlink:href="#linearGradient4044-5" id="linearGradient3060-0" gradientUnits="userSpaceOnUse" x1="15.78776" y1="50.394047" x2="27.641447" y2="39.95837" />
<linearGradient id="linearGradient4044-5">
<stop style="stop-color:#11111b;stop-opacity:1" offset="0" id="stop4046-2" />
<stop style="stop-color:#11111b;stop-opacity:0" offset="1" id="stop4048-9" />
</linearGradient>
<radialGradient xlink:href="#linearGradient12512-2-0" id="radialGradient3062-4" gradientUnits="userSpaceOnUse" cx="55" cy="125" fx="55" fy="125" r="14.375" />
<linearGradient id="linearGradient12512-2-0">
<stop style="stop-color:#cdd6f4;stop-opacity:1" offset="0.0000000" id="stop12513-3-7" />
<stop style="stop-color:#ffd820;stop-opacity:0.89108908" offset="0.5" id="stop12517-1-1" />
<stop style="stop-color:#ff8000;stop-opacity:0" offset="1" id="stop12514-6-57" />
</linearGradient>
<radialGradient r="14.375" fy="125" fx="55" cy="125" cx="55" gradientUnits="userSpaceOnUse" id="radialGradient3086-9" xlink:href="#linearGradient12512-2-0" />
<linearGradient xlink:href="#linearGradient3960" id="linearGradient3966" x1="37.758171" y1="57.301327" x2="21.860462" y2="22.615412" gradientUnits="userSpaceOnUse" />
<linearGradient id="linearGradient3960">
<stop style="stop-color:#bc8009;stop-opacity:1" offset="0" id="stop3962" />
<stop style="stop-color:#f9e2af;stop-opacity:1" offset="1" id="stop3964" />
</linearGradient>
<filter color-interpolation-filters="sRGB" id="filter3980" x="-0.37450271" width="1.7490054" y="-0.37450271" height="1.7490054">
<feGaussianBlur stdDeviation="4.4862304" id="feGaussianBlur3982" />
</filter>
<linearGradient y2="22.615412" x2="21.860462" y1="57.301327" x1="37.758171" gradientUnits="userSpaceOnUse" id="linearGradient4004" xlink:href="#linearGradient3960" />
<linearGradient xlink:href="#linearGradient3960" id="linearGradient4041" gradientUnits="userSpaceOnUse" x1="37.758171" y1="57.301327" x2="21.860462" y2="22.615412" />
<linearGradient xlink:href="#linearGradient3960-7" id="linearGradient4041-9" gradientUnits="userSpaceOnUse" x1="37.758171" y1="57.301327" x2="21.860462" y2="22.615412" />
<linearGradient id="linearGradient3960-7">
<stop style="stop-color:#bc8009;stop-opacity:1" offset="0" id="stop3962-1" />
<stop style="stop-color:#f9e2af;stop-opacity:1" offset="1" id="stop3964-3" />
</linearGradient>
<filter color-interpolation-filters="sRGB" id="filter3980-1" x="-0.37450271" width="1.7490054" y="-0.37450271" height="1.7490054">
<feGaussianBlur stdDeviation="4.4862304" id="feGaussianBlur3982-2" />
</filter>
<linearGradient xlink:href="#linearGradient3960-4" id="linearGradient4041-92" gradientUnits="userSpaceOnUse" x1="37.758171" y1="57.301327" x2="21.860462" y2="22.615412" />
<linearGradient id="linearGradient3960-4">
<stop style="stop-color:#bc8009;stop-opacity:1" offset="0" id="stop3962-4" />
<stop style="stop-color:#f9e2af;stop-opacity:1" offset="1" id="stop3964-5" />
</linearGradient>
<filter color-interpolation-filters="sRGB" id="filter3980-9" x="-0.37450271" width="1.7490054" y="-0.37450271" height="1.7490054">
<feGaussianBlur stdDeviation="4.4862304" id="feGaussianBlur3982-1" />
</filter>
</defs>
<metadata id="metadata2821">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Material_Group</dc:title>
<dc:date>2015-04-19</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Material_Group.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g id="g4035" transform="translate(-56,-2)">
<path transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-12" style="fill:url(#linearGradient4041);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)" d="m 69.375,125 c 0,7.93909 -6.435907,14.375 -14.375,14.375 -7.939093,0 -14.375,-6.43591 -14.375,-14.375 0,-7.93909 6.435907,-14.375 14.375,-14.375 7.939093,0 14.375,6.43591 14.375,14.375 z" id="path12511-77" style="color:#11111b;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;visibility:visible;display:block;filter:url(#filter3980)" />
<path transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-0" style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-3" style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
</g>
<g id="g4035-0" transform="translate(-80,19)">
<path transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-12-6" style="fill:url(#linearGradient4041-9);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)" d="m 69.375,125 c 0,7.93909 -6.435907,14.375 -14.375,14.375 -7.939093,0 -14.375,-6.43591 -14.375,-14.375 0,-7.93909 6.435907,-14.375 14.375,-14.375 7.939093,0 14.375,6.43591 14.375,14.375 z" id="path12511-77-8" style="color:#11111b;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;visibility:visible;display:block;filter:url(#filter3980-1)" />
<path transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-0-7" style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-3-4" style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
</g>
<g id="g4035-2" transform="translate(-50,28)">
<path transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-12-8" style="fill:url(#linearGradient4041-92);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)" d="m 69.375,125 c 0,7.93909 -6.435907,14.375 -14.375,14.375 -7.939093,0 -14.375,-6.43591 -14.375,-14.375 0,-7.93909 6.435907,-14.375 14.375,-14.375 7.939093,0 14.375,6.43591 14.375,14.375 z" id="path12511-77-9" style="color:#11111b;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;visibility:visible;display:block;filter:url(#filter3980-9)" />
<path transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-0-6" style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
<path transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)" d="m 48.597521,39.95837 c 0,11.57372 -9.382354,20.956074 -20.956074,20.956074 -11.57372,0 -20.9560737,-9.382354 -20.9560737,-20.956074 0,-11.57372 9.3823537,-20.956074 20.9560737,-20.956074 11.57372,0 20.956074,9.382354 20.956074,20.956074 z" id="path4042-3-0" style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,857 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2816"
version="1.1"
inkscape:version="0.92.1 r15371"
sodipodi:docname="Arch_Material_Multi.svg">
<defs
id="defs2818">
<linearGradient
inkscape:collect="always"
id="linearGradient4044">
<stop
style="stop-color:#11111b;stop-opacity:1;"
offset="0"
id="stop4046" />
<stop
style="stop-color:#11111b;stop-opacity:0;"
offset="1"
id="stop4048" />
</linearGradient>
<linearGradient
id="linearGradient3681">
<stop
id="stop3697"
offset="0"
style="stop-color:#f8cf78;stop-opacity:1;" />
<stop
style="stop-color:#c35512;stop-opacity:1;"
offset="1"
id="stop3685" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2824" />
<inkscape:perspective
id="perspective3622"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3622-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3653"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3675"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3697"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3720"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3742"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3764"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3785"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3806-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3835"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3614-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3643-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3672-5"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3701-8"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3746"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5224-9"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5224-3"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5288"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3"
inkscape:collect="always" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5323"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<pattern
inkscape:stockid="Stripes 1:1"
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse"
inkscape:collect="always">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:black;stroke:none" />
</pattern>
<inkscape:perspective
id="perspective5361"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5383"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5411"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3687"
x1="37.89756"
y1="41.087898"
x2="4.0605712"
y2="40.168594"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3681"
id="linearGradient3695"
x1="31.777767"
y1="40.24213"
x2="68.442062"
y2="54.041203"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.25023482,-0.66040068,0.68751357,0.24036653,-8.7488565,43.149938)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12512"
id="radialGradient278"
gradientUnits="userSpaceOnUse"
cx="55"
cy="125"
fx="55"
fy="125"
r="14.375" />
<linearGradient
id="linearGradient12512">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop12513" />
<stop
style="stop-color:#f9d487;stop-opacity:0.89108908;"
offset="0.50000000"
id="stop12517" />
<stop
style="stop-color:#f8ca69;stop-opacity:0.0000000;"
offset="1.0000000"
id="stop12514" />
</linearGradient>
<radialGradient
r="14.375"
fy="125"
fx="55"
cy="125"
cx="55"
gradientUnits="userSpaceOnUse"
id="radialGradient4017"
xlink:href="#linearGradient12512"
inkscape:collect="always" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12512-2"
id="radialGradient278-5"
gradientUnits="userSpaceOnUse"
cx="55"
cy="125"
fx="55"
fy="125"
r="14.375" />
<linearGradient
id="linearGradient12512-2">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop12513-3" />
<stop
style="stop-color:#ffd820;stop-opacity:0.89108908;"
offset="0.5"
id="stop12517-1" />
<stop
style="stop-color:#ff8000;stop-opacity:0;"
offset="1"
id="stop12514-6" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4044"
id="linearGradient3060"
gradientUnits="userSpaceOnUse"
x1="15.78776"
y1="50.394047"
x2="27.641447"
y2="39.95837" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12512-2"
id="radialGradient3062"
gradientUnits="userSpaceOnUse"
cx="55"
cy="125"
fx="55"
fy="125"
r="14.375" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4044-2"
id="linearGradient3060-5"
gradientUnits="userSpaceOnUse"
x1="15.78776"
y1="50.394047"
x2="27.641447"
y2="39.95837" />
<linearGradient
inkscape:collect="always"
id="linearGradient4044-2">
<stop
style="stop-color:#11111b;stop-opacity:1;"
offset="0"
id="stop4046-5" />
<stop
style="stop-color:#11111b;stop-opacity:0;"
offset="1"
id="stop4048-4" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12512-2-7"
id="radialGradient3062-5"
gradientUnits="userSpaceOnUse"
cx="55"
cy="125"
fx="55"
fy="125"
r="14.375" />
<linearGradient
id="linearGradient12512-2-7">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop12513-3-4" />
<stop
style="stop-color:#ffd820;stop-opacity:0.89108908;"
offset="0.5"
id="stop12517-1-9" />
<stop
style="stop-color:#ff8000;stop-opacity:0;"
offset="1"
id="stop12514-6-5" />
</linearGradient>
<radialGradient
r="14.375"
fy="125"
fx="55"
cy="125"
cx="55"
gradientUnits="userSpaceOnUse"
id="radialGradient3086"
xlink:href="#linearGradient12512-2-7"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4044-5"
id="linearGradient3060-0"
gradientUnits="userSpaceOnUse"
x1="15.78776"
y1="50.394047"
x2="27.641447"
y2="39.95837" />
<linearGradient
inkscape:collect="always"
id="linearGradient4044-5">
<stop
style="stop-color:#11111b;stop-opacity:1;"
offset="0"
id="stop4046-2" />
<stop
style="stop-color:#11111b;stop-opacity:0;"
offset="1"
id="stop4048-9" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient12512-2-0"
id="radialGradient3062-4"
gradientUnits="userSpaceOnUse"
cx="55"
cy="125"
fx="55"
fy="125"
r="14.375" />
<linearGradient
id="linearGradient12512-2-0">
<stop
style="stop-color:#cdd6f4;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop12513-3-7" />
<stop
style="stop-color:#ffd820;stop-opacity:0.89108908;"
offset="0.5"
id="stop12517-1-1" />
<stop
style="stop-color:#ff8000;stop-opacity:0;"
offset="1"
id="stop12514-6-57" />
</linearGradient>
<radialGradient
r="14.375"
fy="125"
fx="55"
cy="125"
cx="55"
gradientUnits="userSpaceOnUse"
id="radialGradient3086-9"
xlink:href="#linearGradient12512-2-0"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3960"
id="linearGradient3966"
x1="37.758171"
y1="57.301327"
x2="21.860462"
y2="22.615412"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient3960">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3962" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3964" />
</linearGradient>
<filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3980"
x="-0.29294133"
width="1.5858827"
y="-0.44242057"
height="1.8848411">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="4.4862304"
id="feGaussianBlur3982" />
</filter>
<linearGradient
y2="22.615412"
x2="21.860462"
y1="57.301327"
x1="37.758171"
gradientUnits="userSpaceOnUse"
id="linearGradient4004"
xlink:href="#linearGradient3960"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3960"
id="linearGradient4041"
gradientUnits="userSpaceOnUse"
x1="37.758171"
y1="57.301327"
x2="21.860462"
y2="22.615412" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3960-7"
id="linearGradient4041-9"
gradientUnits="userSpaceOnUse"
x1="37.758171"
y1="57.301327"
x2="21.860462"
y2="22.615412" />
<linearGradient
inkscape:collect="always"
id="linearGradient3960-7">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3962-1" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3964-3" />
</linearGradient>
<filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3980-1"
x="-0.29294133"
width="1.5858827"
y="-0.44242057"
height="1.8848411">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="4.4862304"
id="feGaussianBlur3982-2" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3960-4"
id="linearGradient4041-92"
gradientUnits="userSpaceOnUse"
x1="37.758171"
y1="57.301327"
x2="21.860462"
y2="22.615412" />
<linearGradient
inkscape:collect="always"
id="linearGradient3960-4">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3962-4" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3964-5" />
</linearGradient>
<filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3980-9"
x="-0.29294133"
width="1.5858827"
y="-0.44242057"
height="1.8848411">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="4.4862304"
id="feGaussianBlur3982-1" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#cdd6f4"
bordercolor="#38394b"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="6.9457077"
inkscape:cx="11.582505"
inkscape:cy="34.410244"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:object-nodes="true"
inkscape:window-width="1920"
inkscape:window-height="1051"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-nodes="false"
inkscape:snap-global="true">
<inkscape:grid
type="xygrid"
id="grid4006"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Material_Group</dc:title>
<dc:date>2015-04-19</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Material_Group.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g4035-0"
transform="translate(-74,5)">
<circle
transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)"
id="path4042-12-6"
style="fill:url(#linearGradient4041-9);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
inkscape:export-ydpi="33.852203"
inkscape:export-xdpi="33.852203"
inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png"
transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)"
id="path12511-77-8"
style="color:#11111b;display:block;visibility:visible;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;filter:url(#filter3980-1)"
cx="55"
cy="125"
r="14.375" />
<circle
transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)"
id="path4042-0-7"
style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)"
id="path4042-3-4"
style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
</g>
<g
id="g4035"
transform="translate(-65,13)">
<circle
transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)"
id="path4042-12"
style="fill:url(#linearGradient4041);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
inkscape:export-ydpi="33.852203"
inkscape:export-xdpi="33.852203"
inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png"
transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)"
id="path12511-77"
style="color:#11111b;display:block;visibility:visible;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;filter:url(#filter3980)"
cx="55"
cy="125"
r="14.375" />
<circle
transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)"
id="path4042-0"
style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)"
id="path4042-3"
style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
</g>
<g
id="g4035-2"
transform="translate(-55,21)">
<circle
transform="matrix(0.6389479,0,0,0.63940352,79.151188,-6.6213323)"
id="path4042-12-8"
style="fill:url(#linearGradient4041-92);fill-opacity:1;stroke:#664506;stroke-width:3.12903023;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
inkscape:export-ydpi="33.852203"
inkscape:export-xdpi="33.852203"
inkscape:export-filename="/home/jimmac/ximian_art/icons/nautilus/suse93/stock_new-16.png"
transform="matrix(0.1255542,0.12343818,-0.19272145,0.17977458,109.1847,-15.260922)"
id="path12511-77-9"
style="color:#11111b;display:block;visibility:visible;fill:#cdd6f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000024;marker:none;filter:url(#filter3980-9)"
cx="55"
cy="125"
r="14.375" />
<circle
transform="matrix(0.57262634,0,0,0.57262635,81.17178,-3.8812157)"
id="path4042-0-6"
style="fill:none;stroke:#f9e2af;stroke-width:3.49267912;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
<circle
transform="matrix(0.66806408,0,0,0.66806407,78.533742,-7.6947514)"
id="path4042-3-0"
style="fill:none;stroke:#664506;stroke-width:2.99372482;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0"
cx="27.641447"
cy="39.95837"
r="20.956074" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,264 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
id="svg2985"
height="64px"
width="64px">
<defs
id="defs2987">
<marker
style="overflow:visible"
id="Arrow1Lstart"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.8) translate(12.5,0)"
style="fill-rule:evenodd;stroke:#89b4fa;stroke-width:1pt;stroke-opacity:1;fill:#89b4fa;fill-opacity:1"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path4859" />
</marker>
<linearGradient
id="linearGradient3850-6">
<stop
id="stop3852-2"
offset="0"
style="stop-color:#bc8009;stop-opacity:1;" />
<stop
id="stop3854-9"
offset="1"
style="stop-color:#f8c459;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient3858-2">
<stop
id="stop3860-7"
offset="0"
style="stop-color:#ffc900;stop-opacity:1;" />
<stop
id="stop3862-0"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
y2="27.481174"
x2="66.151985"
y1="54.851124"
x1="69.848015"
gradientUnits="userSpaceOnUse"
id="linearGradient3972"
xlink:href="#linearGradient3850-6" />
<linearGradient
y2="26.598274"
x2="55.563385"
y1="56.224525"
x1="59.417618"
gradientUnits="userSpaceOnUse"
id="linearGradient3974"
xlink:href="#linearGradient3858-2" />
<linearGradient
gradientTransform="matrix(-1,0,0,1,111.00667,0.294905)"
y2="21.705095"
x2="66.006668"
y1="53"
x1="69"
gradientUnits="userSpaceOnUse"
id="linearGradient3986"
xlink:href="#linearGradient3850-6" />
<linearGradient
gradientTransform="matrix(-1,0,0,1,111.00667,0.294905)"
y2="20.705095"
x2="55.006672"
y1="54"
x1="57"
gradientUnits="userSpaceOnUse"
id="linearGradient3988"
xlink:href="#linearGradient3858-2" />
<linearGradient
y2="27.481174"
x2="66.151985"
y1="51.449608"
x1="69.018059"
gradientUnits="userSpaceOnUse"
id="linearGradient3986-6"
xlink:href="#linearGradient3850-6-1" />
<linearGradient
id="linearGradient3850-6-1">
<stop
id="stop3852-2-8"
offset="0"
style="stop-color:#bc8009;stop-opacity:1;" />
<stop
id="stop3854-9-7"
offset="1"
style="stop-color:#f8c459;stop-opacity:1" />
</linearGradient>
<linearGradient
y2="26.598274"
x2="55.563385"
y1="52.449608"
x1="57.018063"
gradientUnits="userSpaceOnUse"
id="linearGradient3988-9"
xlink:href="#linearGradient3858-2-2" />
<linearGradient
id="linearGradient3858-2-2">
<stop
id="stop3860-7-0"
offset="0"
style="stop-color:#ffc900;stop-opacity:1;" />
<stop
id="stop3862-0-2"
offset="1"
style="stop-color:#f9e2af;stop-opacity:1" />
</linearGradient>
<linearGradient
y2="21.705095"
x2="66.006668"
y1="53"
x1="69"
gradientTransform="matrix(-1,0,0,1,111.00667,0.294905)"
gradientUnits="userSpaceOnUse"
id="linearGradient4819"
xlink:href="#linearGradient3850-6" />
<linearGradient
y2="20.705095"
x2="55.006672"
y1="54"
x1="57"
gradientTransform="matrix(-1,0,0,1,111.00667,0.294905)"
gradientUnits="userSpaceOnUse"
id="linearGradient4821"
xlink:href="#linearGradient3858-2" />
<linearGradient
y2="27.481174"
x2="66.151985"
y1="51.449608"
x1="69.018059"
gradientUnits="userSpaceOnUse"
id="linearGradient5160"
xlink:href="#linearGradient3850-6-1" />
</defs>
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Antoine Lafr</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Structure</dc:title>
<dc:date>2020-04-11</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_MultipleStructures.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1">
<g
transform="translate(2.010672,1.479651)"
id="g4817">
<path
id="path3927-5-6"
style="color:#11111b;visibility:visible;fill:url(#linearGradient4819);fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
d="m 50,24 0.0067,35.294905 -10,-4 V 24 c 0,-5 -4.017372,-11.479651 -7.017372,-14.479651 L 40.0067,2.294905 C 46.0067,7.294905 50,16 50,24 Z" />
<path
id="path3929-3-0"
style="color:#11111b;visibility:visible;fill:url(#linearGradient4821);fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke"
d="m 59.0067,20.520349 c -0.01737,-5.479651 -4.017372,-16 -10.017372,-19 L 40.0067,2.294905 C 45.0067,6.294905 50,16 50,24 l 0.0067,35.294905 9,-4 z" />
<path
id="path3846-6"
d="m 57.0067,21.294905 v 32.539551 l -5.00003,2.460449 3e-5,-33 z"
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3848-2"
d="m 48,24 -5.9933,-2 v 32 l 5.982628,2.520349 z"
style="fill:none;stroke:#f8c459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
<g
transform="matrix(-1,0,0,1,61.006671,2.294905)"
id="g3978-3">
<g
transform="translate(-22,2)"
id="g3866-9-7">
<g
transform="translate(2.006671,-0.294905)"
id="g5158">
<path
id="path3925-7-3-5"
style="color:#11111b;visibility:visible;fill:#f9e2af;fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 52,23 9,2 9,-2 -8,-2 z" />
<path
id="path3927-5-6-9"
style="color:#11111b;visibility:visible;fill:url(#linearGradient5160);fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 61,25 v 32 l 9,-4 V 23 Z" />
<path
id="path3929-3-0-2"
style="color:#11111b;visibility:visible;fill:url(#linearGradient3988-9);fill-opacity:1;fill-rule:evenodd;stroke:#664506;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
d="m 52,23 9,2 v 32 l -9,-4 z" />
<path
id="path3846-6-2"
d="M 54,26 V 51.539551 L 59,54 V 27 Z"
style="fill:none;stroke:#f9e2af;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3848-2-8"
d="m 63,27 5,-1.550391 v 26.255486 l -4.982658,2.520349 z"
style="fill:none;stroke:#f8c459;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</g>
</g>
<g
transform="translate(0,4)"
id="g5168">
<path
style="fill:#00ffff;stroke:#052459;stroke-width:6;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 8;stroke-dashoffset:0;stroke-opacity:1"
d="M 5,52 V 23"
id="path3878-4" />
<path
style="fill:#00ffff;stroke:#89b4fa;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:4, 8;stroke-dashoffset:0;stroke-opacity:1"
d="M 5,52 V 22"
id="path3878" />
</g>
<g
transform="translate(2)"
id="g5172">
<path
style="fill:none;stroke:#052459;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:4, 8;stroke-dashoffset:0;stroke-opacity:1"
d="M 34,56 V 25 c 0,-3 -5,-9 -8,-12"
id="path3878-2-8" />
<path
style="fill:none;stroke:#89b4fa;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 8;stroke-dashoffset:0;stroke-opacity:1"
d="M 34,56 V 25 c 0,-3 -5,-9 -8,-12"
id="path3878-2" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

680
icons/themed/Arch_Nest.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64px"
height="64px"
id="svg2816"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2818">
<linearGradient
id="linearGradient3633">
<stop
style="stop-color:#fff652;stop-opacity:1;"
offset="0"
id="stop3635" />
<stop
style="stop-color:#ffbf00;stop-opacity:1;"
offset="1"
id="stop3637" />
</linearGradient>
<pattern
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
id="pattern5231"
xlink:href="#Strips1_1-4" />
<pattern
id="Strips1_1-4"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse">
<rect
id="rect4483-4"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:#0b0b0b;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
id="pattern5231-4"
xlink:href="#Strips1_1-6" />
<pattern
id="Strips1_1-6"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse">
<rect
id="rect4483-0"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:#0b0b0b;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
id="pattern5296"
xlink:href="#pattern5231-3" />
<pattern
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
id="pattern5231-3"
xlink:href="#Strips1_1-4-3" />
<pattern
id="Strips1_1-4-3"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse">
<rect
id="rect4483-4-6"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:#0b0b0b;stroke:none" />
</pattern>
<pattern
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
id="pattern5330"
xlink:href="#Strips1_1-9" />
<pattern
id="Strips1_1-9"
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
height="1"
width="2"
patternUnits="userSpaceOnUse">
<rect
id="rect4483-3"
height="2"
width="1"
y="-0.5"
x="0"
style="fill:#0b0b0b;stroke:none" />
</pattern>
<linearGradient
y2="9"
x2="24"
y1="64"
x1="34"
gradientUnits="userSpaceOnUse"
id="linearGradient3120"
xlink:href="#linearGradient3071" />
<linearGradient
id="linearGradient3071">
<stop
id="stop3073"
offset="0"
style="stop-color:#bdbdbd;stop-opacity:1" />
<stop
id="stop3075"
offset="1"
style="stop-color:#cdd6f4;stop-opacity:1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient3874"
id="linearGradient3880"
x1="56"
y1="47"
x2="53"
y2="39"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3874">
<stop
style="stop-color:#bdbdbd;stop-opacity:1"
offset="0"
id="stop3876" />
<stop
style="stop-color:#f1f1f1;stop-opacity:1"
offset="1"
id="stop3878" />
</linearGradient>
<linearGradient
y2="9"
x2="24"
y1="64"
x1="34"
gradientUnits="userSpaceOnUse"
id="linearGradient3075"
xlink:href="#linearGradient3071" />
<linearGradient
xlink:href="#linearGradient3765"
id="linearGradient3771"
x1="40"
y1="68"
x2="34"
y2="12"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(147,-3)" />
<linearGradient
id="linearGradient3765">
<stop
style="stop-color:#bc8009;stop-opacity:1"
offset="0"
id="stop3767" />
<stop
style="stop-color:#f9e2af;stop-opacity:1"
offset="1"
id="stop3769" />
</linearGradient>
</defs>
<metadata
id="metadata2821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>[Yorik van Havre]</dc:title>
</cc:Agent>
</dc:creator>
<dc:title>Arch_Panel_Sheet_Tree</dc:title>
<dc:date>2016-12-17</dc:date>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/Arch_Panel_Sheet_Tree.svg</dc:identifier>
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
<dc:contributor>
<cc:Agent>
<dc:title>[agryson] Alexander Gryson</dc:title>
</cc:Agent>
</dc:contributor>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1">
<path
d="m 2.9999999,9 0,44 L 51,53 61,43 61,9 z"
id="rect3005"
style="color:#11111b;fill:url(#linearGradient3075);fill-opacity:1;fill-rule:evenodd;stroke:#313131;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
style="fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 4.9999999,51 0,-40 L 59,11 l 0,32 -6,8 z"
id="path3096" />
<path
d="m 61,43 c 0,-1 -4,-4 -8,-4 0,0 2,10 -2,14 z"
id="path3778"
style="color:#11111b;fill:url(#linearGradient3880);fill-opacity:1;fill-rule:evenodd;stroke:#4e4e4e;stroke-width:1.95121026;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
style="fill:none;stroke:#bdbdbd;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 53.08551,48.1244 6.65843,-6.453618"
id="path3125" />
<path
d="m 61,43 c 0,-1 -4,-4 -8,-4 0,0 2,10 -2,14 z"
id="path3778-3"
style="color:#11111b;fill:none;stroke:#313131;stroke-width:1.95121026;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
style="color:#11111b;fill:#313131;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 6,12 0,38 16,0 0,-6 6,0 0,6 16,0 0,-38 -16,0 0,6 -6,0 0,-6 z m 4,4 8,0 0,2 c 0,3 1,4 4,4 l 6,0 c 3,0 4,-1 4,-4 l 0,-2 8,0 0,30 -8,0 0,-2 c 0,-3 -1,-4 -4,-4 l -6,0 c -3,0 -4,1 -4,3 l 0,3 -8,0 z"
id="path4207" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

169
icons/themed/Arch_Rebar.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
id="svg249"
version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs3">
<linearGradient
id="linearGradient1">
<stop
style="stop-color:#f9e2af;stop-opacity:1;"
offset="0"
id="stop1" />
<stop
style="stop-color:#bc8009;stop-opacity:1;"
offset="1"
id="stop2" />
</linearGradient>
<linearGradient
id="linearGradient3815">
<stop
id="stop3817"
offset="0"
style="stop-color:#6c7086;stop-opacity:1;" />
<stop
id="stop3819"
offset="1"
style="stop-color:#cdd6f4;stop-opacity:1" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient3815"
id="linearGradient3771"
x1="98"
y1="1047.3622"
x2="81"
y2="993.36218"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-60,-988.36218)" />
<linearGradient
gradientTransform="translate(0,-4)"
xlink:href="#linearGradient3777"
id="linearGradient3783"
x1="64.244514"
y1="51.048775"
x2="39.166134"
y2="22.474567"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3777">
<stop
style="stop-color:#bc8009;stop-opacity:1;"
offset="0"
id="stop3779" />
<stop
style="stop-color:#f9e2af;stop-opacity:1;"
offset="1"
id="stop3781" />
</linearGradient>
<linearGradient
gradientTransform="translate(0,-4)"
xlink:href="#linearGradient3767"
id="linearGradient3773"
x1="39.166134"
y1="61.764099"
x2="3.3382015"
y2="15.331016"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3767">
<stop
style="stop-color:#bc8009;stop-opacity:1;"
offset="0"
id="stop3769" />
<stop
style="stop-color:#f9e2af;stop-opacity:1;"
offset="1"
id="stop3771" />
</linearGradient>
<linearGradient
xlink:href="#linearGradient1"
id="linearGradient2"
x1="3.3382015"
y1="11.331016"
x2="64.244514"
y2="7.7592402"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:rights>
<cc:Agent>
<dc:title>FreeCAD LGPL2+</dc:title>
</cc:Agent>
</dc:rights>
<dc:publisher>
<cc:Agent>
<dc:title>FreeCAD</dc:title>
</cc:Agent>
</dc:publisher>
<dc:relation>https://www.freecad.org/wiki/index.php?title=Artwork</dc:relation>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
style="display:inline">
<path
style="display:inline;fill:url(#linearGradient3771);fill-opacity:1;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 11,3 v 58.00002 h 42 v -48 L 43,3 Z"
id="path2991" />
<path
style="display:inline;fill:none;stroke:#cdd6f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 13,5 V 59.00002 H 51 V 13.81392 L 41.99741,5 Z"
id="path3763" />
<path
style="display:inline;fill:#1e1e2e;fill-opacity:0.392157;stroke:#1e1e2e;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="M 43,3 V 13.00002 H 53 Z"
id="path2993" />
</g>
<g
id="layer3"
style="display:inline"
transform="translate(0,1.9995285)">
<g
id="layer1-3"
transform="matrix(0.55823442,0,0,0.55994556,13.136501,14.655248)"
style="display:inline;stroke-width:3.57725;stroke-dasharray:none">
<path
style="fill:url(#linearGradient2);stroke:#333333;stroke-width:3.57725;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 3.3382015,11.331016 39.166134,18.474567 64.244514,7.75924 28.416933,0.61568843 Z"
id="path2993-0" />
<path
style="fill:url(#linearGradient3783);fill-opacity:1;stroke:#333333;stroke-width:3.57725;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 64.244514,7.75924 V 47.048774 L 39.166134,57.764101 V 18.474567 Z"
id="path2995" />
<path
id="path3825"
d="M 3.3382015,11.331016 39.166134,18.474567 V 57.764101 L 3.3382015,50.62055 Z"
style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient3773);fill-opacity:1;fill-rule:evenodd;stroke:#333333;stroke-width:3.57725;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
</g>
<path
id="path3825-6"
style="display:inline;overflow:visible;visibility:visible;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f8e549;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 17,41.359375 c 5.333333,1.067708 10.666667,2.135417 16,3.203125 0,-5.973958 0,-11.947917 0,-17.921875 -5.333333,-1.067708 -10.666667,-2.135417 -16,-3.203125 0,5.973958 0,11.947917 0,17.921875 z" />
<path
id="path1"
style="fill:none;fill-opacity:1;stroke:#f8e549;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 37,26.320312 c 0,5.881511 0,11.763021 0,17.644532 3.333333,-1.428386 6.666667,-2.856771 10,-4.285156 0,-5.881511 0,-11.763021 0,-17.644532 -3.333333,1.428386 -6.666667,2.856771 -10,4.285156 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

106
icons/themed/Arch_Roof.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

114
icons/themed/Arch_Space.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More