mirror of
https://github.com/penpot/penpot.git
synced 2026-09-10 20:59:12 -04:00
Compare commits
1
Commits
develop
...
issue-11413
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7861631058 |
No files matched your search
@@ -163,6 +163,7 @@
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers)
|
||||
(assoc-in [:workspace-local :edit-path id :moving-handler] moving-handler)
|
||||
(assoc-in [:workspace-local :edit-path id :edited-handler] primary)
|
||||
(cond-> (some? new-prev-handler)
|
||||
(assoc-in [:workspace-local :edit-path id :prev-handler] new-prev-handler)))))))
|
||||
|
||||
@@ -286,7 +287,10 @@
|
||||
content-modifiers))]
|
||||
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] content-modifiers))))))
|
||||
(assoc-in [:workspace-local :edit-path id :content-modifiers] content-modifiers)
|
||||
(cond-> (= 1 (count handler-ids))
|
||||
(assoc-in [:workspace-local :edit-path id :edited-handler]
|
||||
(first handler-ids))))))))
|
||||
|
||||
(defn- move-node-indices
|
||||
[state node-indices from-point to-point]
|
||||
|
||||
@@ -180,6 +180,34 @@
|
||||
|
||||
:else nil)))
|
||||
|
||||
(defn node-handler-ids
|
||||
"Returns a node's curve handlers, its primary handle first."
|
||||
[content node-index]
|
||||
(if-let [[index prefix :as primary] (node-primary-handler content node-index)]
|
||||
(let [[op-idx op-prefix] (path/opposite-index content index prefix)]
|
||||
(if (some? op-idx)
|
||||
[primary [op-idx op-prefix]]
|
||||
[primary]))
|
||||
[]))
|
||||
|
||||
(defn handler-type-reference
|
||||
"Returns the handler that keeps its geometry when a node's handler type changes.
|
||||
|
||||
The other handler adapts to it. Priority: the node's only selected handler,
|
||||
then its most recently edited handler, then its primary handle."
|
||||
[content selection edited-handler node-index]
|
||||
(let [handler-ids (node-handler-ids content node-index)
|
||||
selected (filterv (get selection :handlers #{}) handler-ids)]
|
||||
(cond
|
||||
(= 1 (count selected))
|
||||
(first selected)
|
||||
|
||||
(some #{edited-handler} handler-ids)
|
||||
edited-handler
|
||||
|
||||
:else
|
||||
(first handler-ids))))
|
||||
|
||||
(defn handlers-equal-length?
|
||||
"True when a node's two handlers are the same distance from the node."
|
||||
[content index prefix]
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
(update-in [:workspace-local :edit-path id :selection]
|
||||
#(helpers/remap-selection % old-content new-content))
|
||||
(update-in [:workspace-local :edit-path id :handler-types]
|
||||
#(helpers/remap-handler-types % old-content new-content))))
|
||||
#(helpers/remap-handler-types % old-content new-content))
|
||||
(update-in [:workspace-local :edit-path id] dissoc :edited-handler)))
|
||||
state)))
|
||||
|
||||
ptk/WatchEvent
|
||||
@@ -80,9 +81,11 @@
|
||||
(reduce path/make-curve-point content))))))
|
||||
|
||||
(defn- apply-handler-type-modifiers
|
||||
"Returns modifiers that reshape a node's handlers to `type`."
|
||||
[content node-index type]
|
||||
(if-let [[idx prefix] (helpers/node-primary-handler content node-index)]
|
||||
"Returns modifiers that reshape a node's handlers to `type`.
|
||||
|
||||
`reference` keeps its geometry; the opposite handler adapts to it."
|
||||
[content reference type]
|
||||
(if-let [[idx prefix] reference]
|
||||
(case type
|
||||
:mirror (helpers/move-handler-modifiers content idx prefix true true true 0 0)
|
||||
:aligned (helpers/align-handler-modifiers content idx prefix 0 0)
|
||||
@@ -98,10 +101,13 @@
|
||||
(let [id (st/get-path-id state)
|
||||
content (st/get-path state :content)
|
||||
selection (st/get-selection state id)
|
||||
edited (dm/get-in state [:workspace-local :edit-path id :edited-handler])
|
||||
nodes (helpers/handler-target-nodes content selection)]
|
||||
(if (and (some? content) (seq nodes))
|
||||
(let [modifiers (reduce (fn [acc node-index]
|
||||
(d/deep-merge acc (apply-handler-type-modifiers content node-index type)))
|
||||
(let [reference (helpers/handler-type-reference
|
||||
content selection edited node-index)]
|
||||
(d/deep-merge acc (apply-handler-type-modifiers content reference type))))
|
||||
{} nodes)
|
||||
new-content (path/apply-content-modifiers content modifiers)]
|
||||
(-> (st/set-content state new-content)
|
||||
@@ -146,7 +152,8 @@
|
||||
(update-in [:workspace-local :edit-path id :selection]
|
||||
#(helpers/remap-selection % old-content new-content))
|
||||
(update-in [:workspace-local :edit-path id :handler-types]
|
||||
#(helpers/remap-handler-types % old-content new-content)))))
|
||||
#(helpers/remap-handler-types % old-content new-content))
|
||||
(update-in [:workspace-local :edit-path id] dissoc :edited-handler))))
|
||||
|
||||
(defn remove-segments
|
||||
"Removes segments and opens the path at their endpoints."
|
||||
|
||||
@@ -802,3 +802,78 @@
|
||||
(t/is (empty? (emit-of (mk {:nodes #{3} :segments #{} :handlers #{}})))))))
|
||||
|
||||
;; Path-local undo and redo events use a seeded local stack.
|
||||
|
||||
;; --- Handler type changes pick which handler keeps its geometry
|
||||
|
||||
(defn- aligned-uneven-handlers-content
|
||||
"Returns content whose node (10,0) has aligned handlers at (8,0) and (16,0)."
|
||||
[]
|
||||
(path/content
|
||||
[{:command :move-to :params {:x 0 :y 0}}
|
||||
{:command :curve-to
|
||||
:params {:c1x 2 :c1y 0 :c2x 8 :c2y 0 :x 10 :y 0}}
|
||||
{:command :curve-to
|
||||
:params {:c1x 16 :c1y 0 :c2x 28 :c2y 0 :x 30 :y 0}}]))
|
||||
|
||||
(t/deftest making-handlers-equal-keeps-the-selected-handler-length
|
||||
(let [id (random-uuid)
|
||||
content (aligned-uneven-handlers-content)
|
||||
state (pth/selectable-path-state
|
||||
id content {:nodes #{} :segments #{} :handlers #{[2 :c1]}})
|
||||
result (-> (ptk/update (path.tools/set-handler-type :mirror) state)
|
||||
(path.state/get-path :content))]
|
||||
;; The node is aligned with handlers of unequal length.
|
||||
(t/is (= :aligned (path.helpers/derive-handler-type content 1)))
|
||||
;; The selected handler keeps its length and the opposite one adapts.
|
||||
(t/is (= (gpt/point 16 0) (path/get-handler-point result 2 :c1)))
|
||||
(t/is (= (gpt/point 4 0) (path/get-handler-point result 1 :c2)))))
|
||||
|
||||
(t/deftest making-handlers-equal-falls-back-to-the-last-edited-handler
|
||||
(let [id (random-uuid)
|
||||
content (aligned-uneven-handlers-content)
|
||||
state (-> (pth/selectable-path-state
|
||||
id content {:nodes #{1} :segments #{} :handlers #{}})
|
||||
(assoc-in [:workspace-local :edit-path id :edited-handler]
|
||||
[2 :c1]))
|
||||
result (-> (ptk/update (path.tools/set-handler-type :mirror) state)
|
||||
(path.state/get-path :content))]
|
||||
(t/is (= (gpt/point 16 0) (path/get-handler-point result 2 :c1)))
|
||||
(t/is (= (gpt/point 4 0) (path/get-handler-point result 1 :c2)))))
|
||||
|
||||
(t/deftest making-handlers-equal-uses-the-incoming-handler-with-no-hint
|
||||
(let [id (random-uuid)
|
||||
content (aligned-uneven-handlers-content)
|
||||
state (pth/selectable-path-state
|
||||
id content {:nodes #{1} :segments #{} :handlers #{}})
|
||||
result (-> (ptk/update (path.tools/set-handler-type :mirror) state)
|
||||
(path.state/get-path :content))]
|
||||
(t/is (= (gpt/point 8 0) (path/get-handler-point result 1 :c2)))
|
||||
(t/is (= (gpt/point 12 0) (path/get-handler-point result 2 :c1)))))
|
||||
|
||||
(t/deftest aligning-handlers-takes-the-angle-of-the-selected-handler
|
||||
(let [id (random-uuid)
|
||||
content (path/content
|
||||
[{:command :move-to :params {:x 0 :y 0}}
|
||||
{:command :curve-to
|
||||
:params {:c1x 2 :c1y 0 :c2x 10 :c2y -3 :x 10 :y 0}}
|
||||
{:command :curve-to
|
||||
:params {:c1x 16 :c1y 0 :c2x 28 :c2y 0 :x 30 :y 0}}])
|
||||
state (pth/selectable-path-state
|
||||
id content {:nodes #{} :segments #{} :handlers #{[2 :c1]}})
|
||||
result (-> (ptk/update (path.tools/set-handler-type :aligned) state)
|
||||
(path.state/get-path :content))]
|
||||
;; The selected handler stays put; the opposite one rotates onto its axis
|
||||
;; keeping its own length.
|
||||
(t/is (= (gpt/point 16 0) (path/get-handler-point result 2 :c1)))
|
||||
(t/is (= (gpt/point 7 0) (path/get-handler-point result 1 :c2)))))
|
||||
|
||||
(t/deftest dragging-a-handler-records-it-as-the-last-edited-one
|
||||
(let [id (random-uuid)
|
||||
content (aligned-uneven-handlers-content)
|
||||
state (pth/selectable-path-state
|
||||
id content {:nodes #{} :segments #{} :handlers #{[2 :c1]}})
|
||||
state' (ptk/update
|
||||
(path.edition/modify-selected-handlers id [2 :c1] {} 3 0 :independent false)
|
||||
state)]
|
||||
(t/is (= [2 :c1]
|
||||
(get-in state' [:workspace-local :edit-path id :edited-handler])))))
|
||||
Reference in new issue
Block a user