From 94e72f490e5e090da79cfa546569a0d1f5da17ef Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 30 Sep 2026 11:23:33 -0400 Subject: [PATCH] Asset policy: no Adobe iconography/images; per-asset attribution in ASSETS.md enforced by xtask assets (in ci). Offset Path: fix area loss for offsets larger than curvature radius Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 11 ++ ASSETS.md | 266 +++++++++++++++++++++++++++ CLAUDE.md | 11 ++ NOTICE | 3 +- README.md | 2 +- crates/pathops/src/offset.rs | 84 ++++++++- crates/pathops/tests/prop_pathops.rs | 3 +- xtask/src/assets.rs | 81 ++++++++ xtask/src/main.rs | 6 +- 9 files changed, 460 insertions(+), 7 deletions(-) create mode 100644 ASSETS.md create mode 100644 xtask/src/assets.rs diff --git a/AGENTS.md b/AGENTS.md index 6497a439..63df3522 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,17 @@ DrawCraft is a clean-room, open-source, Rust-native vector illustration app targ ## Non-negotiables - **Clean-room.** Never read, disassemble or copy anything inside the Illustrator bundle (names/listings only). Never copy Adobe icons, artwork, presets or wording beyond feature names. Behaviour comes from public docs and black-box observation of the running app with synthetic documents only (screenshots by window id, stored under `plan/illustrator/screenshots/`, never committed). Never copy GPL/AGPL code (Inkscape, lib2geom…). +- **Assets: no Adobe iconography or images — ever (absolute rule, from the project owner).** + - Never add, copy, trace, redraw-from, embed or ship any icon, image, artwork, cursor, preset, swatch/brush/symbol/pattern/style library, ICC profile or screenshot from Adobe products or from any other source whose licence doesn't allow it. + - Every image, icon, font or other asset must be one of: + - original work created for DrawCraft by a contributor (who licenses it MIT OR Apache-2.0); + - OSI open source; + - public domain / CC0; + - Creative Commons with redistribution allowed. + - **Every asset file must have a row in [`ASSETS.md`](ASSETS.md)** (path, author, source URL, licence, notes). Put licence texts next to the assets (e.g. `assets/fonts/OFL-*.txt`) and summarize them in `NOTICE`. + - `cargo xtask assets` (run by `cargo xtask ci`) fails on any unattributed asset. + - Prefer art generated in code for defaults (swatches, brushes, symbols, patterns, cursors). If the provenance of an asset is unclear, don't add it. + - Reference screenshots of Illustrator stay local under the gitignored `plan/` and are never committed, published or used as assets. - **Everything is a command.** User-visible behaviour = a command in `crates/engine/src/cmd/*` (id, label, menu path, shortcut, params doc, `enabled`, `run`) + tests. Tools emit commands (Begin/Preview/Commit). UI-only commands live in `crates/ui-egui/src/menus.rs` (`UI_COMMANDS`). The control channel and MCP reach all of them. - **Layering** is enforced by `cargo xtask layers`. Nothing below L6 depends on egui/eframe/winit/rfd. - **The UI is thin**: panels read engine state and act through `app.run(id, params)`. Colours come from `theme::Tokens`. diff --git a/ASSETS.md b/ASSETS.md new file mode 100644 index 00000000..f220665d --- /dev/null +++ b/ASSETS.md @@ -0,0 +1,266 @@ +# Asset attribution + +Every non-code asset in this repository (icons, images, fonts, example art, colour profiles, presets) is listed here with its author, source and licence. `cargo xtask assets` (part of `cargo xtask ci`) fails if an asset file is missing from this table. + +**Policy (mandatory):** DrawCraft contains **no Adobe iconography, images, artwork, presets, swatch/brush/symbol/pattern libraries or ICC profiles**. Every asset is either original work by DrawCraft contributors, or third-party material under an open licence (OSI open source, public domain / CC0, or Creative Commons that allows redistribution), and it is attributed below. Screenshots of Adobe software are never committed. + +Generated-in-code art is original and has no file to list. This covers the default swatches, brushes, symbols, patterns, graphic styles, image-trace presets, the app icon (`cargo xtask bundle`) and the vector tool cursors (`crates/ui-egui/src/cursors.rs`). + +| Asset | Author | Source | Licence | Notes | +|---|---|---|---|---| +| `assets/fonts/Inter-Medium.ttf` | Rasmus Andersson / The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/Inter-Regular.ttf` | Rasmus Andersson / The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/Inter-SemiBold.ttf` | Rasmus Andersson / The Inter Project Authors | https://github.com/rsms/inter | OFL-1.1 (`assets/fonts/OFL-Inter.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/JetBrainsMono-Regular.ttf` | The JetBrains Mono Project Authors | https://github.com/JetBrains/JetBrainsMono | OFL-1.1 (`assets/fonts/OFL-JetBrainsMono.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/SourceSans3-Bold.ttf` | Paul D. Hunt / Adobe (released as open source) | https://github.com/adobe-fonts/source-sans | OFL-1.1 (`assets/fonts/OFL-SourceSans3.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/SourceSans3-It.ttf` | Paul D. Hunt / Adobe (released as open source) | https://github.com/adobe-fonts/source-sans | OFL-1.1 (`assets/fonts/OFL-SourceSans3.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/SourceSans3-Regular.ttf` | Paul D. Hunt / Adobe (released as open source) | https://github.com/adobe-fonts/source-sans | OFL-1.1 (`assets/fonts/OFL-SourceSans3.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/SourceSans3-Semibold.ttf` | Paul D. Hunt / Adobe (released as open source) | https://github.com/adobe-fonts/source-sans | OFL-1.1 (`assets/fonts/OFL-SourceSans3.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/SourceSerif4-Regular.ttf` | Frank Grießhammer / Adobe (released as open source) | https://github.com/adobe-fonts/source-serif | OFL-1.1 (`assets/fonts/OFL-SourceSerif4.txt`) | Open-source typeface, not product iconography or artwork | +| `assets/fonts/OFL-Inter.txt` | (licence text) | upstream project | — | | +| `assets/fonts/OFL-JetBrainsMono.txt` | (licence text) | upstream project | — | | +| `assets/fonts/OFL-SourceSans3.txt` | (licence text) | upstream project | — | | +| `assets/fonts/OFL-SourceSerif4.txt` | (licence text) | upstream project | — | | +| `assets/icons/LICENSE-lucide.txt` | (licence text) | upstream project | — | | +| `assets/icons/align-center-horizontal.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-center-vertical.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-end-horizontal.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-end-vertical.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-horizontal-justify-center.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-horizontal-justify-end.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-horizontal-justify-start.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-start-horizontal.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-start-vertical.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-vertical-justify-center.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-vertical-justify-end.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/align-vertical-justify-start.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/arrow-left-right.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/arrow-up-down.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/blend.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/brush.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chart-column.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chevron-down.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chevron-left.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chevron-right.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chevrons-left.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/chevrons-right.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/circle.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/clipboard-paste.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/cloud.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/combine.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/copy.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/dc-actions.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-bottom.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-hcenter.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-left.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-right.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-top.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-al-vcenter.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-align.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-alignto-artboard.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-alignto-key.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-alignto-selection.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-anchor.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-appearance.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-arc.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-arrow-down.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-arrow-up.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-artboard-options.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-artboards.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-blend.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-bloat.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-cap-butt.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-cap-round.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-cap-square.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-clear.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-color-guide.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-crystallize.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-cube.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dash-align.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dash-exact.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-direct.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-bottom.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-hcenter.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-hspace.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-left.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-right.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-top.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-vcenter.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-dist-vspace.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-draw-behind.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-draw-inside.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-draw-normal.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-ellipse.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-fill-none.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-flare.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-folder.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-free-transform.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-fx.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-gamut.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-grad-freeform.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-grad-linear.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-grad-radial.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-gradient.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-graphic-styles.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-grid-view.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-group-select.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-join-bevel.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-join-miter.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-join-round.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-join.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-knife.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-line.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-list-view.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-live-bucket.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-live-select.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-mask-none.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-measure.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-mesh.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-new-fill.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-new-item.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-new-stroke.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-options.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-center.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-justify-all.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-justify-center.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-justify-left.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-justify-right.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-left.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-para-right.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-path-eraser.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pathfinder.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pen-add.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pen-delete.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-perspective.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-crop.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-divide.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-exclude.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-intersect.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-merge.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-minus-back.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-minus-front.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-outline.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-trim.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pf-unite.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-place-symbol.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-polar-grid.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-polygon.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-pucker.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-puppet.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-rearrange.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-rect-grid.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-reference-point.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-remove-brush.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-reshape.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-reverse.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-rotate-view.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-rounded-rect.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-scallop.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-screen-mode.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-selection.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-shape-builder.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-shear.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-smooth.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-stroke-center.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-stroke-inside.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-stroke-outside.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-stroke.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-swap.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-swatch-kinds.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-symbol-sprayer.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-touch-type.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-transform-panel.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-transparency.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-twirl.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-type-area.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-type-path.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-type-vertical.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-width-profile.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-width.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-wrinkle.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-zoom-large.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/dc-zoom-small.svg` | DrawCraft contributors | Original work, drawn for DrawCraft | MIT OR Apache-2.0 | | +| `assets/icons/ellipsis.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/eraser.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/eye-off.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/eye.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/file-plus.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/flip-horizontal-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/flip-vertical-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/folder-open.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/frame.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/grid-3x3.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/group.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/hand.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/hexagon.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/history.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/house.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/image.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/info.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/lasso.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/layers.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/layout-grid.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/library.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/link-2-off.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/link.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/lock-open.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/lock.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/map.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/maximize-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/menu.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/minus.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/monitor.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/move-diagonal-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/move.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/paint-bucket.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/paintbrush.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/palette.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/pen-tool.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/pencil.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/pilcrow.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/pin.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/pipette.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/plus.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/printer.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/redo-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/rotate-3d.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/rotate-ccw.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/rotate-cw.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/ruler.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/save.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/scaling.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/scissors.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/search.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/settings.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/shapes.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/share-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/slice.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/sparkles.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/spline-pointer.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/spline.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/spray-can.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/square-dashed.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/square.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/squares-exclude.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/squares-intersect.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/squares-subtract.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/squares-unite.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/squircle.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/star.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/sun.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/swatch-book.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/text-cursor-input.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/tornado.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/trash-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/triangle.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/type.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/undo-2.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/ungroup.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/wand-sparkles.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/waves.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/x.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `assets/icons/zoom-in.svg` | Lucide Icons and Contributors | https://lucide.dev (lucide-static v1.49.0; header kept in file) | ISC (`assets/icons/LICENSE-lucide.txt`) | | +| `docs/images/drawcraft-hero.png` | DrawCraft contributors | Screenshot of DrawCraft itself (only DrawCraft/Lucide UI icons visible) | MIT OR Apache-2.0 | | +| `docs/images/dusk-poster.png` | DrawCraft contributors | Rendered by DrawCraft from `examples/dusk-poster.drawcraft` | MIT OR Apache-2.0 | | +| `examples/dusk-poster.drawcraft` | DrawCraft contributors | Original artwork built through the DrawCraft command API | MIT OR Apache-2.0 | | +| `examples/dusk-poster.svg` | DrawCraft contributors | SVG export of the above | MIT OR Apache-2.0 | | diff --git a/CLAUDE.md b/CLAUDE.md index 6497a439..63df3522 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,17 @@ DrawCraft is a clean-room, open-source, Rust-native vector illustration app targ ## Non-negotiables - **Clean-room.** Never read, disassemble or copy anything inside the Illustrator bundle (names/listings only). Never copy Adobe icons, artwork, presets or wording beyond feature names. Behaviour comes from public docs and black-box observation of the running app with synthetic documents only (screenshots by window id, stored under `plan/illustrator/screenshots/`, never committed). Never copy GPL/AGPL code (Inkscape, lib2geom…). +- **Assets: no Adobe iconography or images — ever (absolute rule, from the project owner).** + - Never add, copy, trace, redraw-from, embed or ship any icon, image, artwork, cursor, preset, swatch/brush/symbol/pattern/style library, ICC profile or screenshot from Adobe products or from any other source whose licence doesn't allow it. + - Every image, icon, font or other asset must be one of: + - original work created for DrawCraft by a contributor (who licenses it MIT OR Apache-2.0); + - OSI open source; + - public domain / CC0; + - Creative Commons with redistribution allowed. + - **Every asset file must have a row in [`ASSETS.md`](ASSETS.md)** (path, author, source URL, licence, notes). Put licence texts next to the assets (e.g. `assets/fonts/OFL-*.txt`) and summarize them in `NOTICE`. + - `cargo xtask assets` (run by `cargo xtask ci`) fails on any unattributed asset. + - Prefer art generated in code for defaults (swatches, brushes, symbols, patterns, cursors). If the provenance of an asset is unclear, don't add it. + - Reference screenshots of Illustrator stay local under the gitignored `plan/` and are never committed, published or used as assets. - **Everything is a command.** User-visible behaviour = a command in `crates/engine/src/cmd/*` (id, label, menu path, shortcut, params doc, `enabled`, `run`) + tests. Tools emit commands (Begin/Preview/Commit). UI-only commands live in `crates/ui-egui/src/menus.rs` (`UI_COMMANDS`). The control channel and MCP reach all of them. - **Layering** is enforced by `cargo xtask layers`. Nothing below L6 depends on egui/eframe/winit/rfd. - **The UI is thin**: panels read engine state and act through `app.run(id, params)`. Colours come from `theme::Tokens`. diff --git a/NOTICE b/NOTICE index 3130911b..87526664 100644 --- a/NOTICE +++ b/NOTICE @@ -1,7 +1,8 @@ DrawCraft Copyright the DrawCraft contributors. Licensed under MIT OR Apache-2.0. -This product bundles third-party material under the following licences: +This product bundles third-party material under the following licences. Per-file attribution +(author, source, licence) for every asset is in ASSETS.md. No Adobe icons, images or artwork are included. - Lucide icons (assets/icons/*.svg except dc-*.svg) — ISC License. See assets/icons/LICENSE-lucide.txt. - DrawCraft icons (assets/icons/dc-*.svg) — original work, MIT OR Apache-2.0. diff --git a/README.md b/README.md index fd13c3ea..1249bfe7 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,6 @@ Open-source, pure-Rust, clean-room creative tools — each engine-first, cross-p ## License -MIT OR Apache-2.0. Bundled fonts are OFL; Lucide icons are ISC — see [`NOTICE`](NOTICE). +MIT OR Apache-2.0. Bundled fonts are OFL; Lucide icons are ISC; all other icons and art are original. Per-asset attribution: [`ASSETS.md`](ASSETS.md) (see also [`NOTICE`](NOTICE)). DrawCraft is an independent project and is not affiliated with or endorsed by Adobe. "Adobe", "Illustrator", "Photoshop", "Premiere Pro", "Lightroom" and "Acrobat" are trademarks of Adobe Inc., used here only to describe compatibility and workflow familiarity. diff --git a/crates/pathops/src/offset.rs b/crates/pathops/src/offset.rs index 873e6450..1c821d57 100644 --- a/crates/pathops/src/offset.rs +++ b/crates/pathops/src/offset.rs @@ -5,7 +5,9 @@ use kurbo::{BezPath, PathEl, Stroke, StrokeOpts}; pub use kurbo::{Cap, Join}; -use crate::boolean::{Arrangement, DEFAULT_PRECISION, Tidy, all_contours_to_path, fill_bezpath, normalize_bez}; +use linesweeper::topology::{ContourIdx, Contours}; + +use crate::boolean::{Arrangement, DEFAULT_PRECISION, Tidy, all_contours_to_path, contours_to_path, fill_bezpath, normalize_bez}; /// Flattening/fit tolerance handed to kurbo's stroker. const STROKE_TOL: f64 = 1e-3; @@ -72,5 +74,83 @@ pub fn offset_path(path: &PathData, delta: f64, join: Join, miter_limit: f64) -> return PathData::default(); }; let c = if delta >= 0.0 { arr.contours(|m| m[0] || m[1] || m[2]) } else { arr.contours(|m| m[0] && !m[1]) }; - all_contours_to_path(&c, &Tidy::free(DEFAULT_PRECISION)) + // When `d` exceeds a curvature radius the stroker's inner offset inverts and its loops cancel + // winding, leaving faces uncovered that are really within `d` of the path. Every face of the + // true offset is bounded by arrangement edges, so a contour is spurious iff a point deep inside + // it lies closer than `d`: holes when growing, islands when insetting. + let src = path.to_bezpath(); + let drop_outer = delta < 0.0; + let spurious: Vec = + c.contours().map(|k| k.outer == drop_outer && deep_point(&k.path).is_some_and(|p| dist_to(&src, p) < d * (1.0 - 1e-4) - 1e-6)).collect(); + if !spurious.contains(&true) { + return all_contours_to_path(&c, &Tidy::free(DEFAULT_PRECISION)); + } + let keep = (0..spurious.len()).filter(|&i| !has_marked_ancestor(&c, i, &spurious)).map(ContourIdx); + contours_to_path(&c, keep, &Tidy::free(DEFAULT_PRECISION)) +} + +fn has_marked_ancestor(c: &Contours, mut i: usize, marked: &[bool]) -> bool { + loop { + if marked[i] { + return true; + } + match c[ContourIdx(i)].parent { + Some(ContourIdx(p)) => i = p, + None => return false, + } + } +} + +/// A point well inside a closed contour: the midpoint of the widest span on the horizontal line +/// through its vertical centre. +fn deep_point(bp: &BezPath) -> Option { + use kurbo::Shape; + let r = bp.bounding_box(); + if !(r.width() > 0.0 && r.height() > 0.0) { + return None; + } + let mut best: Option<(f64, kurbo::Point)> = None; + for f in [0.5, 0.3, 0.7] { + let y = r.y0 + r.height() * f; + let mut xs = vec![]; + let mut prev: Option = None; + let mut start = kurbo::Point::ZERO; + let edge = |a: kurbo::Point, b: kurbo::Point, xs: &mut Vec| { + if (a.y <= y) != (b.y <= y) { + xs.push(a.x + (y - a.y) / (b.y - a.y) * (b.x - a.x)); + } + }; + kurbo::flatten(bp.iter(), 0.01 * r.width().min(r.height()).max(1e-6), |el| match el { + PathEl::MoveTo(p) => { + start = p; + prev = Some(p); + } + PathEl::LineTo(p) => { + if let Some(a) = prev { + edge(a, p, &mut xs); + } + prev = Some(p); + } + PathEl::ClosePath => { + if let Some(a) = prev { + edge(a, start, &mut xs); + } + prev = Some(start); + } + _ => {} + }); + xs.sort_by(f64::total_cmp); + for w in xs.chunks_exact(2) { + let span = w[1] - w[0]; + if best.is_none_or(|(b, _)| span > b) { + best = Some((span, kurbo::Point::new((w[0] + w[1]) / 2.0, y))); + } + } + } + best.map(|(_, p)| p) +} + +fn dist_to(bp: &BezPath, p: kurbo::Point) -> f64 { + use kurbo::ParamCurveNearest; + bp.segments().map(|s| s.nearest(p, 1e-6).distance_sq).fold(f64::INFINITY, f64::min).sqrt() } diff --git a/crates/pathops/tests/prop_pathops.rs b/crates/pathops/tests/prop_pathops.rs index c9c3c9e0..c6ba9cc8 100644 --- a/crates/pathops/tests/prop_pathops.rs +++ b/crates/pathops/tests/prop_pathops.rs @@ -243,8 +243,7 @@ fn bug_simplify_adds_anchors() { } #[test] -#[ignore = "BUG (open): offset by more than ~the shape's diameter loses area (inner stroke loop cancels winding)"] -fn bug_offset_large_delta_loses_area() { +fn offset_large_delta_keeps_area() { for (r, d) in [(1.0, 5.0), (1.0, 3.0), (10.0, 30.0)] { let p = drawcraft_geom::shapes::ellipse(Rect::new(0.0, 0.0, 2.0 * r, 2.0 * r)); let got = area(&offset_path(&p, d, Join::Round, 4.0), NZ); diff --git a/xtask/src/assets.rs b/xtask/src/assets.rs new file mode 100644 index 00000000..122c7928 --- /dev/null +++ b/xtask/src/assets.rs @@ -0,0 +1,81 @@ +//! `cargo xtask assets`: every non-code asset must be attributed in `ASSETS.md`. +//! +//! Scans all git-tracked (and untracked, not ignored) files with an asset extension, plus +//! everything under `assets/`, `docs/images/` and `examples/`, and fails if a path is not listed +//! as `` `path` `` in ASSETS.md. See the asset policy in AGENTS.md. + +use std::path::Path; +use std::process::Command; + +const ASSET_EXT: &[&str] = &[ + "png", + "jpg", + "jpeg", + "gif", + "webp", + "bmp", + "tif", + "tiff", + "ico", + "icns", + "svg", + "pdf", + "ai", + "eps", + "psd", + "icc", + "icm", + "ttf", + "otf", + "woff", + "woff2", + "ase", + "aco", + "abr", + "drawcraft", + "mp4", + "wav", + "mp3", +]; +const ASSET_DIRS: &[&str] = &["assets/", "docs/images/", "examples/"]; + +pub fn is_asset(path: &str) -> bool { + let ext = Path::new(path).extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase).unwrap_or_default(); + ASSET_DIRS.iter().any(|d| path.starts_with(d)) || ASSET_EXT.contains(&ext.as_str()) +} + +/// Paths that need an ASSETS.md entry but lack one. +pub fn missing(files: &[String], assets_md: &str) -> Vec { + files.iter().filter(|f| is_asset(f) && !assets_md.contains(&format!("`{f}`"))).cloned().collect() +} + +pub fn run(root: &Path) -> Result<(), String> { + let out = Command::new("git") + .current_dir(root) + .args(["ls-files", "--cached", "--others", "--exclude-standard"]) + .output() + .map_err(|e| format!("git ls-files: {e}"))?; + let files: Vec = String::from_utf8_lossy(&out.stdout).lines().filter(|l| root.join(l).exists()).map(str::to_owned).collect(); + let md = std::fs::read_to_string(root.join("ASSETS.md")).map_err(|e| format!("ASSETS.md: {e}"))?; + let miss = missing(&files, &md); + if miss.is_empty() { + println!("assets: all {} asset files attributed in ASSETS.md", files.iter().filter(|f| is_asset(f)).count()); + Ok(()) + } else { + Err(format!("{} asset file(s) lack an ASSETS.md entry (author, source, licence):\n {}", miss.len(), miss.join("\n "))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detects_unattributed_assets() { + let files = vec!["assets/icons/a.svg".to_string(), "crates/x/src/lib.rs".into(), "docs/images/b.png".into(), "tests/fixture.png".into()]; + let md = "| `assets/icons/a.svg` | me | here | MIT |"; + assert_eq!(missing(&files, md), vec!["docs/images/b.png".to_string(), "tests/fixture.png".into()]); + assert!(!is_asset("crates/x/src/lib.rs")); + assert!(is_asset("assets/fonts/OFL.txt")); + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 90dde793..b8a7932a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -3,6 +3,7 @@ //! Pure Rust (std + serde_json). External tools (`cargo`, `curl`, `tar`) are //! invoked through `std::process::Command`. +mod assets; mod bundle; mod layers; mod stats; @@ -14,9 +15,10 @@ const USAGE: &str = "\ usage: cargo xtask commands: + assets check that every icon/image/font/asset is attributed in ASSETS.md layers enforce the crate dependency layering (plan/architecture.md §3) wasm cargo check --target wasm32-unknown-unknown for the wasm-safe crates - ci fmt --check, clippy -D warnings, test, layers, wasm (stops at first failure) + ci fmt --check, clippy -D warnings, test, assets, layers, wasm (stops at first failure) corpus [--download] show where test corpora live; --download fetches PngSuite into corpus/pngsuite bundle build dist/DrawCraft.app (macOS) with an icon rendered by DrawCraft @@ -27,6 +29,7 @@ fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); let rest: Vec<&str> = args.iter().skip(1).map(String::as_str).collect(); let result = match args.first().map(String::as_str) { + Some("assets") => assets::run(&root()), Some("layers") => cmd_layers(), Some("wasm") => cmd_wasm(), Some("ci") => cmd_ci(), @@ -166,6 +169,7 @@ fn cmd_ci() -> Result<(), String> { run(c, "cargo test --workspace") }), ), + ("assets", Box::new(|| assets::run(&root()))), ("layers", Box::new(cmd_layers)), ("wasm", Box::new(cmd_wasm)), ];