mirror of
https://github.com/penpot/penpot.git
synced 2025-03-15 17:21:17 -05:00
🎉 Improved reusability of drawing functions
This commit is contained in:
parent
05366eac6f
commit
275f6e3dc2
6 changed files with 154 additions and 77 deletions
|
@ -26,6 +26,14 @@
|
|||
[v]
|
||||
(instance? Point v))
|
||||
|
||||
(defn ^boolean point-like?
|
||||
[{:keys [x y] :as v}]
|
||||
(and (map? v)
|
||||
(not (nil? x))
|
||||
(not (nil? y))
|
||||
(number? x)
|
||||
(number? y)))
|
||||
|
||||
(defn point
|
||||
"Create a Point instance."
|
||||
([] (Point. 0 0))
|
||||
|
@ -37,6 +45,9 @@
|
|||
(number? v)
|
||||
(Point. v v)
|
||||
|
||||
(point-like? v)
|
||||
(Point. (:x v) (:y v))
|
||||
|
||||
:else
|
||||
(throw (ex-info "Invalid arguments" {:v v}))))
|
||||
([x y]
|
||||
|
|
|
@ -21,7 +21,10 @@
|
|||
segments)
|
||||
|
||||
(defn content->points [content]
|
||||
(mapv #(gpt/point (-> % :params :x) (-> % :params :y)) content))
|
||||
(->> content
|
||||
(map #(when (-> % :params :x) (gpt/point (-> % :params :x) (-> % :params :y))))
|
||||
(remove nil?)
|
||||
(into [])))
|
||||
|
||||
;; https://medium.com/@Acegikmo/the-ever-so-lovely-b%C3%A9zier-curve-eb27514da3bf
|
||||
;; https://en.wikipedia.org/wiki/Bernstein_polynomial
|
||||
|
@ -105,6 +108,7 @@
|
|||
(let [calc-extremities
|
||||
(fn [command prev]
|
||||
(case (:command command)
|
||||
:close-path []
|
||||
:move-to [(command->point command)]
|
||||
|
||||
;; If it's a line we add the beginning point and endpoint
|
||||
|
|
|
@ -226,7 +226,8 @@
|
|||
:content content
|
||||
:points points
|
||||
:selrect selrect
|
||||
:rotation rotation)))
|
||||
;;:rotation rotation
|
||||
)))
|
||||
|
||||
(defn apply-transform-curve
|
||||
[shape transform]
|
||||
|
|
|
@ -11,12 +11,101 @@
|
|||
(:require
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[app.common.math :as mth]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.data :as d]
|
||||
[app.util.geom.path :as ugp]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.data.workspace.drawing.common :as common]))
|
||||
|
||||
;;;;
|
||||
|
||||
(def close-path-distance 5)
|
||||
|
||||
(defn seek-start-path [content]
|
||||
(->> content
|
||||
reverse
|
||||
(d/seek (fn [{cmd :command}] (= cmd :move-to)))
|
||||
:params))
|
||||
|
||||
(defn next-node
|
||||
"Calculates the next-node to be inserted."
|
||||
[shape position prev-point prev-handler]
|
||||
(let [last-command (-> shape :content last :command)
|
||||
start-point (-> shape :content seek-start-path)
|
||||
|
||||
add-line? (and prev-point (not prev-handler) (not= last-command :close-path))
|
||||
add-curve? (and prev-point prev-handler (not= last-command :close-path))
|
||||
close-path? (and start-point
|
||||
(< (mth/abs (gpt/distance (gpt/point start-point)
|
||||
(gpt/point position)))
|
||||
close-path-distance))]
|
||||
(cond
|
||||
close-path? {:command :close-path
|
||||
:params []}
|
||||
add-line? {:command :line-to
|
||||
:params position}
|
||||
add-curve? {:command :curve-to
|
||||
:params (ugp/make-curve-params position prev-handler)}
|
||||
:else {:command :move-to
|
||||
:params position})))
|
||||
|
||||
(defn append-node
|
||||
"Creates a new node in the path. Usualy used when drawing."
|
||||
[shape position prev-point prev-handler]
|
||||
(let [command (next-node shape position prev-point prev-handler)]
|
||||
(as-> shape $
|
||||
(update $ :content (fnil conj []) command)
|
||||
(update $ :selrect (gsh/content->selrect (:content $))))))
|
||||
|
||||
(defn suffix-keyword [kw suffix]
|
||||
(let [strkw (if kw (name kw) "")]
|
||||
(keyword (str strkw suffix))))
|
||||
|
||||
;; handler-type => :prev :next
|
||||
(defn move-handler [shape index handler-type match-opposite? position]
|
||||
(let [content (:content shape)
|
||||
[command next-command] (-> (d/with-next content) (nth index))
|
||||
|
||||
update-command
|
||||
(fn [{cmd :command params :params :as command} param-prefix prev-command]
|
||||
(if (#{:line-to :curve-to} cmd)
|
||||
(let [command (if (= cmd :line-to)
|
||||
{:command :curve-to
|
||||
:params (ugp/make-curve-params params (:params prev-command))}
|
||||
command)]
|
||||
(-> command
|
||||
(update :params assoc
|
||||
(suffix-keyword param-prefix "x") (:x position)
|
||||
(suffix-keyword param-prefix "y") (:y position))))
|
||||
command))
|
||||
|
||||
update-content
|
||||
(fn [shape index prefix]
|
||||
(if (contains? (:content shape) index)
|
||||
(let [prev-command (get-in shape [:content (dec index)])]
|
||||
(update-in shape [:content index] update-command prefix prev-command))
|
||||
|
||||
shape))]
|
||||
|
||||
(cond-> shape
|
||||
(= :prev handler-type)
|
||||
(update-content index :c2)
|
||||
|
||||
(and (= :next handler-type) next-command)
|
||||
(update-content (inc index) :c1)
|
||||
|
||||
match-opposite?
|
||||
(move-handler
|
||||
index
|
||||
(if (= handler-type :prev) :next :prev)
|
||||
false
|
||||
(ugp/opposite-handler (gpt/point (:params command))
|
||||
(gpt/point position))))))
|
||||
|
||||
|
||||
;;;;
|
||||
(defn finish-event? [{:keys [type shift] :as event}]
|
||||
(or (= event ::end-path-drawing)
|
||||
(= event :interrupt)
|
||||
|
@ -74,92 +163,37 @@
|
|||
(ptk/reify ::add-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [point {:x x :y y}
|
||||
{:keys [last-point prev-handler]} (get-in state [:workspace-drawing :object])
|
||||
|
||||
command (cond
|
||||
(and last-point (not prev-handler))
|
||||
{:command :line-to
|
||||
:params point}
|
||||
|
||||
(and last-point prev-handler)
|
||||
{:command :curve-to
|
||||
:params (ugp/make-curve-params point prev-handler)}
|
||||
|
||||
:else
|
||||
nil)
|
||||
]
|
||||
(-> state
|
||||
(assoc-in [:workspace-drawing :object :preview] command))))))
|
||||
(let [position (gpt/point x y)
|
||||
{:keys [last-point prev-handler] :as shape} (get-in state [:workspace-drawing :object])
|
||||
command (next-node shape position last-point prev-handler)]
|
||||
(assoc-in state [:workspace-drawing :object :preview] command)))))
|
||||
|
||||
(defn add-node [{:keys [x y]}]
|
||||
(ptk/reify ::add-node
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [point {:x x :y y}
|
||||
{:keys [last-point prev-handler]} (get-in state [:workspace-drawing :object])
|
||||
|
||||
command (cond
|
||||
(and last-point (not prev-handler))
|
||||
{:command :line-to
|
||||
:params point}
|
||||
|
||||
(and last-point prev-handler)
|
||||
{:command :curve-to
|
||||
:params (ugp/make-curve-params point prev-handler)}
|
||||
|
||||
:else
|
||||
{:command :move-to
|
||||
:params point})
|
||||
]
|
||||
(-> state
|
||||
(assoc-in [:workspace-drawing :object :last-point] point)
|
||||
(update-in [:workspace-drawing :object] dissoc :prev-handler)
|
||||
(update-in [:workspace-drawing :object :content] (fnil conj []) command)
|
||||
(update-in [:workspace-drawing :object] calculate-selrect))))))
|
||||
(let [position (gpt/point x y)
|
||||
{:keys [last-point prev-handler]} (get-in state [:workspace-drawing :object])]
|
||||
(update-in
|
||||
state
|
||||
[:workspace-drawing :object]
|
||||
#(-> %
|
||||
(append-node position last-point prev-handler)
|
||||
(assoc :last-point position)
|
||||
(dissoc :prev-handler)))))))
|
||||
|
||||
(defn drag-handler [{:keys [x y]}]
|
||||
(ptk/reify ::drag-handler
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [change-handler (fn [content]
|
||||
(let [last-idx (dec (count content))
|
||||
last (get content last-idx nil)
|
||||
prev (get content (dec last-idx) nil)
|
||||
{last-x :x last-y :y} (:params last)
|
||||
opposite (when last (ugp/opposite-handler (gpt/point last-x last-y) (gpt/point x y)))]
|
||||
|
||||
(cond
|
||||
(and prev (= (:command last) :line-to))
|
||||
(-> content
|
||||
(assoc last-idx {:command :curve-to
|
||||
:params {:x (-> last :params :x)
|
||||
:y (-> last :params :y)
|
||||
:c1x (-> prev :params :x)
|
||||
:c1y (-> prev :params :y)
|
||||
:c2x (-> last :params :x)
|
||||
:c2y (-> last :params :y)}})
|
||||
(update-in
|
||||
[last-idx :params]
|
||||
#(-> %
|
||||
(assoc :c2x (:x opposite)
|
||||
:c2y (:y opposite)))))
|
||||
|
||||
(= (:command last) :curve-to)
|
||||
(update-in content
|
||||
[last-idx :params]
|
||||
#(-> %
|
||||
(assoc :c2x (:x opposite)
|
||||
:c2y (:y opposite))))
|
||||
:else
|
||||
content))
|
||||
|
||||
|
||||
)
|
||||
handler (gpt/point x y)]
|
||||
(let [position (gpt/point x y)
|
||||
shape (get-in state [:workspace-drawing :object])
|
||||
index (dec (count (:content shape)))]
|
||||
(-> state
|
||||
(update-in [:workspace-drawing :object :content] change-handler)
|
||||
(assoc-in [:workspace-drawing :object :drag-handler] handler))))))
|
||||
(update-in [:workspace-drawing :object] move-handler index :next true position)
|
||||
(assoc-in [:workspace-drawing :object :drag-handler] position))))))
|
||||
|
||||
(defn finish-drag []
|
||||
(ptk/reify ::finish-drag
|
||||
|
|
|
@ -118,6 +118,33 @@
|
|||
(into {}))
|
||||
m1))
|
||||
|
||||
(defn with-next
|
||||
"Given a collectin will return a new collection where each element
|
||||
is paried with the next item in the collection
|
||||
(with-next (range 5)) => [[0 1] [1 2] [2 3] [3 4] [4 nil]"
|
||||
[coll]
|
||||
(map vector
|
||||
coll
|
||||
(concat [] (rest coll) [nil])))
|
||||
|
||||
(defn with-prev
|
||||
"Given a collectin will return a new collection where each element
|
||||
is paried with the previous item in the collection
|
||||
(with-prev (range 5)) => [[0 nil] [1 0] [2 1] [3 2] [4 3]"
|
||||
[coll]
|
||||
(map vector
|
||||
coll
|
||||
(concat [nil] coll)))
|
||||
|
||||
(defn with-prev-next
|
||||
"Given a collection will return a new collection where every item is paired
|
||||
with the previous and the next item of a collection
|
||||
(with-prev-next (range 5)) => [[0 nil 1] [1 0 2] [2 1 3] [3 2 4] [4 3 nil]"
|
||||
[coll]
|
||||
(map vector
|
||||
coll
|
||||
(concat [nil] coll)
|
||||
(concat [] (rest coll) [nil])))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Numbers Parsing
|
||||
|
@ -221,3 +248,4 @@
|
|||
;; nil
|
||||
;; (throw e#)))))))
|
||||
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
(defn shape-snap-points
|
||||
[shape]
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
|
||||
(case (:type shape)
|
||||
:frame (-> shape :selrect frame-snap-points)
|
||||
(:path :curve) (-> shape :selrect selrect-snap-points)
|
||||
|
|
Loading…
Add table
Reference in a new issue