mirror of
https://github.com/PR0M3TH3AN/Artstr-Studio.git
synced 2026-11-06 05:30:52 +00:00
Merge branch 'pen-tool-phase-d': Illustrator-parity pen / select tools, Pathfinder, Align, Eyedropper, Free Transform, Marquee
Bundles the design-tool polish arc: - Pen tool: Direct Selection (A) as a separate tool, Cmd/Ctrl temp toggle, hover cursor cues, Shift constrain, auto-add / auto-delete on segment / anchor, click-vs-drag convert, Alt-drag asymmetric pull, Alt-click curved ↔ pointed (drops / grows handles), multi- anchor select via Shift-click + marquee, arrow-nudge anchors, drag-segment to bow the curve, continue-an-open-path from its endpoint, Spacebar reposition + Alt mid-draw asymmetric during create-drag, Alt-drag handle breaks symmetry in DSel, Cmd+J join, Cmd+Alt+J average, Path Actions side panel with batch buttons. - Eyedropper (I) via native window.EyeDropper. - Align / distribute panel for layer selection. - Free Transform rotation handle on single + multi-layer selection bounding boxes, Shift snaps to 15°. - Pathfinder ops (Unite / Subtract / Intersect / Exclude), backed by vendor/polygon-clipping.min.js. - Marquee box-select on the V tool with marching-ants animation. - Double-click a vector shape with V to dive into Direct Selection; DSel empty-click deselects the layer entirely. - Misc: pen draft stroke width now matches the panel value, anchor + handle glyphs shrunk closer to Illustrator size, Direct Select arrow tinted light blue. - Docs: PEN_TOOL_FEATURE.md + TODO.md updated.
This commit is contained in:
commit
e25180346b
4 files changed
+1964
-68
No files matched your search
@@ -8,6 +8,29 @@ Running list of deferred work. Detailed specs for the larger items live in
|
||||
_(Empty — see "Shipped (recent)" below for the items that just
|
||||
landed. Drop new items in here as they come up.)_
|
||||
|
||||
## Design-tool follow-ups (Illustrator parity arc)
|
||||
|
||||
The pen / select / pathfinder bundle (`docs/PEN_TOOL_FEATURE.md`)
|
||||
landed with these intentional gaps:
|
||||
|
||||
- **Smart Guides.** Snap-to-anchor and snap-to-edge alignment with
|
||||
on-canvas distance hints while dragging. Biggest "feels professional"
|
||||
upgrade still on the table; deserves its own arc.
|
||||
- **Gradients + drop shadows.** Linear / radial gradient fills and a
|
||||
basic drop-shadow / glow effect on the appearance panel. Mostly an
|
||||
SVG `<defs>` + `filter` addition.
|
||||
- **Scissors (C) + Outline text + Command palette (Cmd+K).** Three
|
||||
small wins: split a path at a click, convert text to editable pen
|
||||
paths (font-independent for Nostr export), searchable action list.
|
||||
- **Flip H / V.** Prototyped during the Align panel arc but pulled
|
||||
out — needs the renderer to honor per-layer scaleX / scaleY.
|
||||
- **Pathfinder limitations to lift.** Today: refuses rotated shapes,
|
||||
flattens curves, only the first polygon's outer ring stays
|
||||
pen-editable post-op. Rotation support is the most user-facing of
|
||||
these; curve fidelity would need a Bézier-aware clipper.
|
||||
- **Pixel-art frame timeline.** Onion skin + frame list + FPS control.
|
||||
Largest open arc; transformative if pixel art is a focus.
|
||||
|
||||
## QR code layer — remaining phases
|
||||
|
||||
Phases A and B shipped (the `qr` layer type: data, error-correction level,
|
||||
|
||||
@@ -24,12 +24,18 @@ tool-mode system with flanking toolbars to host it.
|
||||
shape-layer commit.
|
||||
- **Phase C** — done. Pen click/drag anchors, rubber-band preview, close /
|
||||
Enter / dbl-click / Esc, closed-fill-on-close, `shape.pen.nodes` stored.
|
||||
- **Phase D** — partial. Editable anchors + Bézier handles for selected
|
||||
`tool:'pen'` paths; drag-to-move anchor (carries handles) and drag-to-move
|
||||
handle, regenerates `d`, one undo per drag. *Deferred:* add/delete anchor,
|
||||
corner↔smooth toggle, click-segment-to-insert, and bbox-refit when an
|
||||
anchor is dragged past the original 0–100 viewBox (path clips at the box
|
||||
edge until refit lands). Rotated paths are not node-editable in v1.
|
||||
- **Phase D** — done (close-out 2026-06-01). On top of the existing
|
||||
drag-anchor / drag-handle path: click on a segment between anchors
|
||||
inserts a new anchor via a de Casteljau cubic split (curve shape
|
||||
preserved exactly); right-click or Backspace on a selected anchor
|
||||
deletes it; Alt-click on an anchor toggles between **corner**
|
||||
(handles move independently, square anchor glyph) and **smooth**
|
||||
(handles stay mirrored across the anchor, circle anchor glyph);
|
||||
dragging a node or handle past the original 0–100 viewBox refits
|
||||
the layer's bounding rect + remaps coordinates so the path stays
|
||||
visually anchored without clipping. The currently-selected anchor
|
||||
renders with an amber stroke so Backspace-to-delete is unambiguous.
|
||||
Rotated paths are still not node-editable in v1.
|
||||
- **Phase E** — done. `layer.hidden` flag, eye toggle in the layer list, all
|
||||
six renderers + Canvas2D export skip hidden layers.
|
||||
- **Unified vector editing** — done. With the Pen tool active, the options
|
||||
@@ -42,16 +48,92 @@ tool-mode system with flanking toolbars to host it.
|
||||
arcs, abs & rel). *Not convertible:* multi-element SVGs and compound
|
||||
(multi-subpath) paths — the panel shows a hint instead.
|
||||
|
||||
### Implementation status — 2026-06-02 (Illustrator parity arc)
|
||||
|
||||
A subsequent arc brought the pen / select tools much closer to
|
||||
Adobe Illustrator's feel. The tool-mode system from Phases A–E is
|
||||
unchanged; the new work plugs into it:
|
||||
|
||||
- **Direct Selection (A)** as a separate tool from Select (V),
|
||||
matching Illustrator's V/A split. Cmd / Ctrl held from any
|
||||
non-pen tool temporarily flips the effective tool to Direct
|
||||
Selection so a quick anchor tweak doesn't require dropping the
|
||||
current tool.
|
||||
- **Hover cursor cues**: − over an anchor in Pen mode (auto-delete),
|
||||
+ over a segment (auto-add), the "continue" pointer over an
|
||||
endpoint of an open path, grab over a segment in Direct Select
|
||||
(bow the curve).
|
||||
- **Pen auto-add / auto-delete**: clicking a segment with no
|
||||
modifier inserts an anchor at the click point; clicking an
|
||||
existing anchor deletes it. Drag (instead of click) on an anchor
|
||||
converts it on the fly — pulls symmetric handles out. Alt-drag
|
||||
pulls an asymmetric corner.
|
||||
- **Convert anchor** (Alt-click in Pen or Direct Select): flips
|
||||
curved ↔ pointed completely. Curved → pointed drops both handles
|
||||
for a real sharp corner; pointed → curved grows symmetric handles
|
||||
along the chord through the anchor (1/3 of chord length).
|
||||
- **Shift constrains** pen segments to 0 / 45 / 90° while drawing
|
||||
and previewing the next click.
|
||||
- **Multi-anchor selection**: Shift-click anchors to add / remove
|
||||
them from a selection Set. Drag a marquee inside the editable
|
||||
path to box-select anchors. Arrow keys nudge every selected
|
||||
anchor (1 px, 10 px with Shift) instead of the whole layer.
|
||||
Backspace deletes the whole anchor selection at once.
|
||||
- **Drag a segment** to bow the curve (Direct Select). Equal-split
|
||||
delta across the two inner control points so the cursor tracks
|
||||
the grabbed point; smooth anchors on either end keep their
|
||||
handles mirrored.
|
||||
- **Continue an open path**: with the Pen tool, click the start /
|
||||
end anchor of an open path to resume drawing from there. The
|
||||
source layer is hidden during the draft; on commit (Enter / close
|
||||
/ Escape) the new path takes its place, preserving z-order,
|
||||
target, fill and stroke.
|
||||
- **Spacebar reposition** during pen create-drag: slides the anchor
|
||||
itself instead of growing handles; release Space to resume
|
||||
handle-pulling from the new position.
|
||||
- **Alt mid-draw** during pen create-drag: only the outgoing handle
|
||||
is pulled, incoming side stays at the anchor — asymmetric corner
|
||||
for tangent-then-corner transitions without lifting the pen.
|
||||
- **Alt-drag handle** in Direct Selection: breaks symmetry, the
|
||||
opposite handle stays put.
|
||||
- **Path keyboard ops**: Cmd+J closes an open path when both
|
||||
endpoints are selected; Cmd+Alt+J averages selected anchors to
|
||||
their centroid.
|
||||
- **Path Actions side panel**: when Pen or Direct Selection is
|
||||
active and a pen path is selected, a sidebar block surfaces the
|
||||
same ops as buttons — Pointed corner, Curved smooth, Delete
|
||||
anchor, Average, Join endpoints, Close / Open path, Reverse
|
||||
direction. Buttons auto-disable when the action would be a no-op.
|
||||
|
||||
### Non-goals (v1)
|
||||
- A raster paint layer (true pixel buffers). Explicitly out of scope — the
|
||||
app's design-as-JSON model and Nostr-friendliness depend on storing
|
||||
instructions, not bitmaps. A `paint` layer type could be a separate future
|
||||
feature; this doc does not pursue it.
|
||||
- Pressure / tilt / velocity-sensitive brushes.
|
||||
- Boolean path operations (union / subtract / intersect).
|
||||
- Moving the existing shape-primitive menu into the left toolbar. The
|
||||
sidebar "+ Shape" menu stays; pen/pencil are additive.
|
||||
|
||||
### Adjacent design-tool features (shipped in the same arc)
|
||||
- **Eyedropper (I)** — native EyeDropper picker, samples any pixel
|
||||
on screen to the selected layer's fill (or stroke with Shift).
|
||||
- **Align & distribute panel** — six align ops + horizontal /
|
||||
vertical distribute on the layer selection (uses the selection
|
||||
bbox for 2+, target bounds for a single layer).
|
||||
- **Free Transform rotation handle** above both the single-layer
|
||||
selection bbox and the multi-layer selection box; Shift snaps to
|
||||
15°. Multi-layer rotates each layer's centre around the bbox
|
||||
centre AND advances each layer's own rotation.
|
||||
- **Pathfinder** — Unite / Subtract / Intersect / Exclude on 2+
|
||||
selected shape layers, backed by `vendor/polygon-clipping.min.js`.
|
||||
Curves are flattened to polygon approximations. Rotated layers
|
||||
are not supported yet.
|
||||
- **Marquee box-select** on empty canvas with the Select (V) tool —
|
||||
marching-ants rectangle, Shift adds to the existing selection.
|
||||
- **Double-click dive-into-edit**: a vector shape under the Select
|
||||
tool flips to Direct Selection on dbl-click and auto-converts
|
||||
primitives to editable pen paths so anchors show immediately.
|
||||
|
||||
---
|
||||
|
||||
## Why this fits the repo
|
||||
|
||||
+1828
-61
File diff suppressed because it is too large.
Load diff
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).polygonClipping=e()}(this,(function(){"use strict";
|
||||
/**
|
||||
* splaytree v3.1.2
|
||||
* Fast Splay tree for Node and browser
|
||||
*
|
||||
* @author Alexander Milevski <[email protected]>
|
||||
* @license MIT
|
||||
* @preserve
|
||||
*/
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */function t(t,e){var n,r,i,o,s={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function l(o){return function(l){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return s.label++,{value:o[1],done:!1};case 5:s.label++,r=o[1],o=[0];continue;case 7:o=s.ops.pop(),s.trys.pop();continue;default:if(!(i=s.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){s.label=o[1];break}if(6===o[0]&&s.label<i[1]){s.label=i[1],i=o;break}if(i&&s.label<i[2]){s.label=i[2],s.ops.push(o);break}i[2]&&s.ops.pop(),s.trys.pop();continue}o=e.call(t,s)}catch(t){o=[6,t],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,l])}}}var e=function(t,e){this.next=null,this.key=t,this.data=e,this.left=null,this.right=null};function n(t,e){return t>e?1:t<e?-1:0}function r(t,n,r){for(var i=new e(null,null),o=i,s=i;;){var l=r(t,n.key);if(l<0){if(null===n.left)break;if(r(t,n.left.key)<0){var h=n.left;if(n.left=h.right,h.right=n,null===(n=h).left)break}s.left=n,s=n,n=n.left}else{if(!(l>0))break;if(null===n.right)break;if(r(t,n.right.key)>0){h=n.right;if(n.right=h.left,h.left=n,null===(n=h).right)break}o.right=n,o=n,n=n.right}}return o.right=n.left,s.left=n.right,n.left=i.right,n.right=i.left,n}function i(t,n,i,o){var s=new e(t,n);if(null===i)return s.left=s.right=null,s;var l=o(t,(i=r(t,i,o)).key);return l<0?(s.left=i.left,s.right=i,i.left=null):l>=0&&(s.right=i.right,s.left=i,i.right=null),s}function o(t,e,n){var i=null,o=null;if(e){var s=n((e=r(t,e,n)).key,t);0===s?(i=e.left,o=e.right):s<0?(o=e.right,e.right=null,i=e):(i=e.left,e.left=null,o=e)}return{left:i,right:o}}function s(t,e,n,r,i){if(t){r(e+(n?"└── ":"├── ")+i(t)+"\n");var o=e+(n?" ":"│ ");t.left&&s(t.left,o,!1,r,i),t.right&&s(t.right,o,!0,r,i)}}var l=function(){function l(t){void 0===t&&(t=n),this._root=null,this._size=0,this._comparator=t}return l.prototype.insert=function(t,e){return this._size++,this._root=i(t,e,this._root,this._comparator)},l.prototype.add=function(t,n){var i=new e(t,n);null===this._root&&(i.left=i.right=null,this._size++,this._root=i);var o=this._comparator,s=r(t,this._root,o),l=o(t,s.key);return 0===l?this._root=s:(l<0?(i.left=s.left,i.right=s,s.left=null):l>0&&(i.right=s.right,i.left=s,s.right=null),this._size++,this._root=i),this._root},l.prototype.remove=function(t){this._root=this._remove(t,this._root,this._comparator)},l.prototype._remove=function(t,e,n){var i;return null===e?null:0===n(t,(e=r(t,e,n)).key)?(null===e.left?i=e.right:(i=r(t,e.left,n)).right=e.right,this._size--,i):e},l.prototype.pop=function(){var t=this._root;if(t){for(;t.left;)t=t.left;return this._root=r(t.key,this._root,this._comparator),this._root=this._remove(t.key,this._root,this._comparator),{key:t.key,data:t.data}}return null},l.prototype.findStatic=function(t){for(var e=this._root,n=this._comparator;e;){var r=n(t,e.key);if(0===r)return e;e=r<0?e.left:e.right}return null},l.prototype.find=function(t){return this._root&&(this._root=r(t,this._root,this._comparator),0!==this._comparator(t,this._root.key))?null:this._root},l.prototype.contains=function(t){for(var e=this._root,n=this._comparator;e;){var r=n(t,e.key);if(0===r)return!0;e=r<0?e.left:e.right}return!1},l.prototype.forEach=function(t,e){for(var n=this._root,r=[],i=!1;!i;)null!==n?(r.push(n),n=n.left):0!==r.length?(n=r.pop(),t.call(e,n),n=n.right):i=!0;return this},l.prototype.range=function(t,e,n,r){for(var i=[],o=this._comparator,s=this._root;0!==i.length||s;)if(s)i.push(s),s=s.left;else{if(o((s=i.pop()).key,e)>0)break;if(o(s.key,t)>=0&&n.call(r,s))return this;s=s.right}return this},l.prototype.keys=function(){var t=[];return this.forEach((function(e){var n=e.key;return t.push(n)})),t},l.prototype.values=function(){var t=[];return this.forEach((function(e){var n=e.data;return t.push(n)})),t},l.prototype.min=function(){return this._root?this.minNode(this._root).key:null},l.prototype.max=function(){return this._root?this.maxNode(this._root).key:null},l.prototype.minNode=function(t){if(void 0===t&&(t=this._root),t)for(;t.left;)t=t.left;return t},l.prototype.maxNode=function(t){if(void 0===t&&(t=this._root),t)for(;t.right;)t=t.right;return t},l.prototype.at=function(t){for(var e=this._root,n=!1,r=0,i=[];!n;)if(e)i.push(e),e=e.left;else if(i.length>0){if(e=i.pop(),r===t)return e;r++,e=e.right}else n=!0;return null},l.prototype.next=function(t){var e=this._root,n=null;if(t.right){for(n=t.right;n.left;)n=n.left;return n}for(var r=this._comparator;e;){var i=r(t.key,e.key);if(0===i)break;i<Line truncated
|
||||
//# sourceMappingURL=polygon-clipping.umd.min.js.map
|
||||
Reference in new issue
Block a user