mirror of
https://github.com/penpot/penpot.git
synced 2025-03-12 07:41:43 -05:00
✨ Adds point to curves tool
This commit is contained in:
parent
421b30c1d8
commit
e81b1b8115
4 changed files with 90 additions and 7 deletions
|
@ -253,7 +253,16 @@
|
||||||
(and (mth/almost-zero? x)
|
(and (mth/almost-zero? x)
|
||||||
(mth/almost-zero? y)))
|
(mth/almost-zero? y)))
|
||||||
|
|
||||||
|
(defn line-val
|
||||||
|
"Given a line with two points p1-p2 and a 'percent'. Returns the point in the vector
|
||||||
|
generated by these two points. For example: for p1=(0,0) p2=(1,1) and v=0.25 will return
|
||||||
|
the point (0.25, 0.25)"
|
||||||
|
[p1 p2 v]
|
||||||
|
(let [v (-> (to-vec p1 p2)
|
||||||
|
(scale v))]
|
||||||
|
(add p1 v)))
|
||||||
|
|
||||||
;; --- Debug
|
;; --- Debug
|
||||||
|
|
||||||
(defmethod pp/simple-dispatch Point [obj] (pr obj))
|
(defmethod pp/simple-dispatch Point [obj] (pr obj))
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,20 @@
|
||||||
|
|
||||||
(gpt/point (coord-v :x) (coord-v :y))))
|
(gpt/point (coord-v :x) (coord-v :y))))
|
||||||
|
|
||||||
|
(defn curve-split
|
||||||
|
"Splits a curve into two at the given parametric value `t`.
|
||||||
|
Calculates the Casteljau's algorithm intermediate points"
|
||||||
|
[start end h1 h2 t]
|
||||||
|
|
||||||
|
(let [p1 (gpt/line-val start h1 t)
|
||||||
|
p2 (gpt/line-val h1 h2 t)
|
||||||
|
p3 (gpt/line-val h2 end t)
|
||||||
|
p4 (gpt/line-val p1 p2 t)
|
||||||
|
p5 (gpt/line-val p2 p3 t)
|
||||||
|
sp (gpt/line-val p4 p5 t)]
|
||||||
|
[[start sp p1 p4]
|
||||||
|
[sp end p5 p3]]))
|
||||||
|
|
||||||
;; https://pomax.github.io/bezierinfo/#extremities
|
;; https://pomax.github.io/bezierinfo/#extremities
|
||||||
(defn curve-extremities
|
(defn curve-extremities
|
||||||
"Given a cubic bezier cube finds its roots in t. This are the extremities
|
"Given a cubic bezier cube finds its roots in t. This are the extremities
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.main.data.workspace.path.common :as common]
|
[app.main.data.workspace.path.common :as common]
|
||||||
[app.main.data.workspace.path.state :as st]
|
[app.main.data.workspace.path.state :as st]
|
||||||
[app.util.geom.path :as ugp]
|
[app.util.geom.path :as ugp]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
@ -41,8 +42,40 @@
|
||||||
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
||||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true}))))))
|
(rx/of (dwc/commit-changes rch uch {:commit-local? true}))))))
|
||||||
|
|
||||||
|
(defn split-segments [[start end cmd]]
|
||||||
|
(case (:command cmd)
|
||||||
|
:line-to [cmd (ugp/split-line-to start cmd 0.5)]
|
||||||
|
:curve-to [cmd (ugp/split-curve-to start cmd 0.5)]
|
||||||
|
:close-path [cmd [(ugp/make-line-to (gpt/line-val start end 0.5))
|
||||||
|
cmd]]
|
||||||
|
nil))
|
||||||
|
|
||||||
(defn add-node []
|
(defn add-node []
|
||||||
(ptk/reify ::add-node))
|
(ptk/reify ::add-node
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
|
||||||
|
(let [id (st/get-path-id state)
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
shape (get-in state (st/get-path state))
|
||||||
|
selected-points (get-in state [:workspace-local :edit-path id :selected-points] #{})
|
||||||
|
content (:content shape)
|
||||||
|
|
||||||
|
|
||||||
|
cmd-changes (->> (ugp/get-segments content selected-points)
|
||||||
|
(into {}
|
||||||
|
(comp (map split-segments)
|
||||||
|
(filter (comp not nil?)))))
|
||||||
|
|
||||||
|
process-segments (fn [command]
|
||||||
|
(if (contains? cmd-changes command)
|
||||||
|
(get cmd-changes command)
|
||||||
|
[command]))
|
||||||
|
|
||||||
|
new-content (into [] (mapcat process-segments) content)
|
||||||
|
|
||||||
|
[rch uch] (changes/generate-path-changes page-id shape (:content shape) new-content)]
|
||||||
|
(rx/of (dwc/commit-changes rch uch {:commit-local? true}))))))
|
||||||
|
|
||||||
(defn remove-node []
|
(defn remove-node []
|
||||||
(ptk/reify ::remove-node))
|
(ptk/reify ::remove-node))
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes.path :as gshp]
|
||||||
[app.util.a2c :refer [a2c]]
|
[app.util.a2c :refer [a2c]]
|
||||||
[app.util.geom.path-impl-simplify :as impl-simplify]
|
[app.util.geom.path-impl-simplify :as impl-simplify]
|
||||||
[app.util.svg :as usvg]
|
[app.util.svg :as usvg]
|
||||||
|
@ -64,6 +65,11 @@
|
||||||
(cond-> result
|
(cond-> result
|
||||||
(not (empty? current)) (conj current))))))
|
(not (empty? current)) (conj current))))))
|
||||||
|
|
||||||
|
(defn command->point [command]
|
||||||
|
(when-not (nil? command)
|
||||||
|
(let [{{:keys [x y]} :params} command]
|
||||||
|
(gpt/point x y))))
|
||||||
|
|
||||||
(defn command->param-list [command]
|
(defn command->param-list [command]
|
||||||
(let [params (:params command)]
|
(let [params (:params command)]
|
||||||
(case (:command command)
|
(case (:command command)
|
||||||
|
@ -387,6 +393,12 @@
|
||||||
(mapv command->string)
|
(mapv command->string)
|
||||||
(str/join "")))
|
(str/join "")))
|
||||||
|
|
||||||
|
(defn make-line-to [to]
|
||||||
|
{:command :line-to
|
||||||
|
:relative false
|
||||||
|
:params {:x (:x to)
|
||||||
|
:y (:y to)}})
|
||||||
|
|
||||||
(defn make-curve-params
|
(defn make-curve-params
|
||||||
([point]
|
([point]
|
||||||
(make-curve-params point point point))
|
(make-curve-params point point point))
|
||||||
|
@ -401,6 +413,26 @@
|
||||||
:c2x (:x h2)
|
:c2x (:x h2)
|
||||||
:c2y (:y h2)}))
|
:c2y (:y h2)}))
|
||||||
|
|
||||||
|
(defn make-curve-to [to h1 h2]
|
||||||
|
{:command :curve-to
|
||||||
|
:relative false
|
||||||
|
:params (make-curve-params to h1 h2)})
|
||||||
|
|
||||||
|
(defn split-line-to [from-p cmd val]
|
||||||
|
(let [to-p (command->point cmd)
|
||||||
|
sp (gpt/line-val from-p to-p val)]
|
||||||
|
[(make-line-to sp) cmd]))
|
||||||
|
|
||||||
|
(defn split-curve-to [from-p cmd val]
|
||||||
|
(let [params (:params cmd)
|
||||||
|
end (gpt/point (:x params) (:y params))
|
||||||
|
h1 (gpt/point (:c1x params) (:c1y params))
|
||||||
|
h2 (gpt/point (:c2x params) (:c2y params))
|
||||||
|
[[_ to1 h11 h21]
|
||||||
|
[_ to2 h12 h22]] (gshp/curve-split from-p end h1 h2 val)]
|
||||||
|
[(make-curve-to to1 h11 h21)
|
||||||
|
(make-curve-to to2 h12 h22)]))
|
||||||
|
|
||||||
(defn opposite-handler
|
(defn opposite-handler
|
||||||
"Calculates the coordinates of the opposite handler"
|
"Calculates the coordinates of the opposite handler"
|
||||||
[point handler]
|
[point handler]
|
||||||
|
@ -441,11 +473,6 @@
|
||||||
(let [content (if (vector? content) content (into [] content))]
|
(let [content (if (vector? content) content (into [] content))]
|
||||||
(reduce apply-to-index content modifiers))))
|
(reduce apply-to-index content modifiers))))
|
||||||
|
|
||||||
(defn command->point [command]
|
|
||||||
(when-not (nil? command)
|
|
||||||
(let [{{:keys [x y]} :params} command]
|
|
||||||
(gpt/point x y))))
|
|
||||||
|
|
||||||
(defn content->points [content]
|
(defn content->points [content]
|
||||||
(->> content
|
(->> content
|
||||||
(map #(when (-> % :params :x) (gpt/point (-> % :params :x) (-> % :params :y))))
|
(map #(when (-> % :params :x) (gpt/point (-> % :params :x) (-> % :params :y))))
|
||||||
|
@ -638,7 +665,7 @@
|
||||||
|
|
||||||
segments (cond-> segments
|
segments (cond-> segments
|
||||||
is-segment?
|
is-segment?
|
||||||
(conj [prev-point cur-point]))]
|
(conj [prev-point cur-point cur-cmd]))]
|
||||||
|
|
||||||
(if (some? cur-cmd)
|
(if (some? cur-cmd)
|
||||||
(recur segments
|
(recur segments
|
||||||
|
|
Loading…
Add table
Reference in a new issue