mirror of
https://github.com/penpot/penpot.git
synced 2025-05-06 00:05:53 +02:00
✨ Improved make curve options
This commit is contained in:
parent
de11e85d2b
commit
c7683dfd80
8 changed files with 305 additions and 141 deletions
|
@ -213,12 +213,12 @@
|
||||||
(let [v-length (length v)]
|
(let [v-length (length v)]
|
||||||
(divide v (point v-length v-length))))
|
(divide v (point v-length v-length))))
|
||||||
|
|
||||||
(defn project [v1 v2]
|
(defn project
|
||||||
|
"V1 perpendicular projection on vector V2"
|
||||||
|
[v1 v2]
|
||||||
(let [v2-unit (unit v2)
|
(let [v2-unit (unit v2)
|
||||||
scalar-projection (dot v1 (unit v2))]
|
scalar-proj (dot v1 v2-unit)]
|
||||||
(multiply
|
(scale v2-unit scalar-proj)))
|
||||||
v2-unit
|
|
||||||
(point scalar-projection scalar-projection))))
|
|
||||||
|
|
||||||
(defn center-points
|
(defn center-points
|
||||||
"Centroid of a group of points"
|
"Centroid of a group of points"
|
||||||
|
@ -264,7 +264,34 @@
|
||||||
(scale v))]
|
(scale v))]
|
||||||
(add p1 v)))
|
(add p1 v)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn rotate
|
||||||
|
"Rotates the point around center with an angle"
|
||||||
|
[{px :x py :y} {cx :x cy :y} angle]
|
||||||
|
(let [angle (mth/radians angle)
|
||||||
|
|
||||||
|
x (+ (* (mth/cos angle) (- px cx))
|
||||||
|
(* (mth/sin angle) (- py cy) -1)
|
||||||
|
cx)
|
||||||
|
|
||||||
|
y (+ (* (mth/sin angle) (- px cx))
|
||||||
|
(* (mth/cos angle) (- py cy))
|
||||||
|
cy)]
|
||||||
|
(point x y)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn scale-from
|
||||||
|
"Moves a point in the vector that creates with center with a scale
|
||||||
|
value"
|
||||||
|
[point center value]
|
||||||
|
(add point
|
||||||
|
(-> (to-vec center point)
|
||||||
|
(unit)
|
||||||
|
(scale value))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Debug
|
;; --- Debug
|
||||||
|
|
||||||
(defmethod pp/simple-dispatch Point [obj] (pr obj))
|
(defmethod pp/simple-dispatch Point [obj] (pr obj))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,54 +58,51 @@
|
||||||
(update-in (st/get-path state) helpers/append-node position last-point prev-handler))
|
(update-in (st/get-path state) helpers/append-node position last-point prev-handler))
|
||||||
state)))))
|
state)))))
|
||||||
|
|
||||||
(defn start-drag-handler []
|
(defn drag-handler
|
||||||
(ptk/reify ::start-drag-handler
|
([{:keys [x y alt? shift?] :as position}]
|
||||||
ptk/UpdateEvent
|
(drag-handler nil nil :c1 position))
|
||||||
(update [_ state]
|
|
||||||
(let [content (get-in state (st/get-path state :content))
|
|
||||||
index (dec (count content))
|
|
||||||
command (get-in state (st/get-path state :content index :command))
|
|
||||||
|
|
||||||
make-curve
|
([position index prefix {:keys [x y alt? shift?]}]
|
||||||
(fn [command]
|
|
||||||
(let [params (upc/make-curve-params
|
|
||||||
(get-in content [index :params])
|
|
||||||
(get-in content [(dec index) :params]))]
|
|
||||||
(-> command
|
|
||||||
(assoc :command :curve-to :params params))))]
|
|
||||||
|
|
||||||
(cond-> state
|
|
||||||
(= command :line-to)
|
|
||||||
(update-in (st/get-path state :content index) make-curve))))))
|
|
||||||
|
|
||||||
(defn drag-handler [{:keys [x y alt? shift?]}]
|
|
||||||
(ptk/reify ::drag-handler
|
(ptk/reify ::drag-handler
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
shape (get-in state (st/get-path state))
|
content (get-in state (st/get-path state :content))
|
||||||
content (:content shape)
|
|
||||||
index (dec (count content))
|
index (or index (count content))
|
||||||
node-position (upc/command->point (nth content index))
|
prefix (or prefix :c1)
|
||||||
|
position (or position (upc/command->point (nth content (dec index))))
|
||||||
|
|
||||||
|
old-handler (helpers/handler->point content index prefix)
|
||||||
|
|
||||||
handler-position (cond-> (gpt/point x y)
|
handler-position (cond-> (gpt/point x y)
|
||||||
shift? (helpers/position-fixed-angle node-position))
|
shift? (helpers/position-fixed-angle position))
|
||||||
{dx :x dy :y} (gpt/subtract handler-position node-position)
|
|
||||||
|
{dx :x dy :y} (if (some? old-handler)
|
||||||
|
(gpt/add (gpt/to-vec old-handler position)
|
||||||
|
(gpt/to-vec position handler-position))
|
||||||
|
(gpt/to-vec position handler-position))
|
||||||
|
|
||||||
match-opposite? (not alt?)
|
match-opposite? (not alt?)
|
||||||
modifiers (helpers/move-handler-modifiers content (inc index) :c1 match-opposite? dx dy)]
|
|
||||||
|
modifiers (helpers/move-handler-modifiers content index prefix match-opposite? match-opposite? dx dy)]
|
||||||
(-> state
|
(-> state
|
||||||
(update-in [:workspace-local :edit-path id :content-modifiers] merge modifiers)
|
(update-in [:workspace-local :edit-path id :content-modifiers] merge modifiers)
|
||||||
(assoc-in [:workspace-local :edit-path id :prev-handler] handler-position)
|
(assoc-in [:workspace-local :edit-path id :drag-handler] handler-position)))))))
|
||||||
(assoc-in [:workspace-local :edit-path id :drag-handler] handler-position))))))
|
|
||||||
|
|
||||||
(defn finish-drag []
|
(defn finish-drag []
|
||||||
(ptk/reify ::finish-drag
|
(ptk/reify ::finish-drag
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
|
|
||||||
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
||||||
|
content (-> (get-in state (st/get-path state :content))
|
||||||
|
(upc/apply-content-modifiers modifiers))
|
||||||
|
|
||||||
handler (get-in state [:workspace-local :edit-path id :drag-handler])]
|
handler (get-in state [:workspace-local :edit-path id :drag-handler])]
|
||||||
(-> state
|
(-> state
|
||||||
(update-in (st/get-path state :content) upc/apply-content-modifiers modifiers)
|
(assoc-in (st/get-path state :content) content)
|
||||||
(update-in [:workspace-local :edit-path id] dissoc :drag-handler)
|
(update-in [:workspace-local :edit-path id] dissoc :drag-handler)
|
||||||
(update-in [:workspace-local :edit-path id] dissoc :content-modifiers)
|
(update-in [:workspace-local :edit-path id] dissoc :content-modifiers)
|
||||||
(assoc-in [:workspace-local :edit-path id :prev-handler] handler)
|
(assoc-in [:workspace-local :edit-path id :prev-handler] handler)
|
||||||
|
@ -136,16 +133,20 @@
|
||||||
snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])
|
snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])
|
||||||
points (upg/content->points content)
|
points (upg/content->points content)
|
||||||
|
|
||||||
|
handlers (-> (upc/content->handlers content)
|
||||||
|
(get position))
|
||||||
|
|
||||||
|
[idx prefix] (when (= (count handlers) 1) (first handlers))
|
||||||
|
|
||||||
drag-events-stream
|
drag-events-stream
|
||||||
(->> (streams/position-stream snap-toggled points)
|
(->> (streams/position-stream snap-toggled points)
|
||||||
(rx/take-until stop-stream)
|
(rx/take-until stop-stream)
|
||||||
(rx/map #(drag-handler %)))]
|
(rx/map #(drag-handler position idx prefix %)))]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (add-node position))
|
(rx/of (add-node position))
|
||||||
(streams/drag-stream
|
(streams/drag-stream
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (start-drag-handler))
|
|
||||||
drag-events-stream
|
drag-events-stream
|
||||||
(rx/of (finish-drag))
|
(rx/of (finish-drag))
|
||||||
(rx/of (close-path-drag-end))))
|
(rx/of (close-path-drag-end))))
|
||||||
|
@ -180,7 +181,6 @@
|
||||||
(rx/of (add-node position))
|
(rx/of (add-node position))
|
||||||
(streams/drag-stream
|
(streams/drag-stream
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (start-drag-handler))
|
|
||||||
drag-events
|
drag-events
|
||||||
(rx/of (finish-drag)))))))))
|
(rx/of (finish-drag)))))))))
|
||||||
|
|
||||||
|
@ -204,7 +204,6 @@
|
||||||
(rx/of (add-node down-event))
|
(rx/of (add-node down-event))
|
||||||
(streams/drag-stream
|
(streams/drag-stream
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (start-drag-handler))
|
|
||||||
drag-events
|
drag-events
|
||||||
(rx/of (finish-drag)))))))
|
(rx/of (finish-drag)))))))
|
||||||
|
|
||||||
|
|
|
@ -25,37 +25,23 @@
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
(defn modify-point [index prefix dx dy]
|
|
||||||
(ptk/reify ::modify-point
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [id (get-in state [:workspace-local :edition])
|
|
||||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])]
|
|
||||||
(-> state
|
|
||||||
(update-in [:workspace-local :edit-path id :content-modifiers (inc index)] assoc
|
|
||||||
:c1x dx :c1y dy)
|
|
||||||
(update-in [:workspace-local :edit-path id :content-modifiers index] assoc
|
|
||||||
:x dx :y dy :c2x dx :c2y dy))))))
|
|
||||||
|
|
||||||
(defn modify-handler [id index prefix dx dy match-opposite?]
|
(defn modify-handler [id index prefix dx dy match-opposite?]
|
||||||
(ptk/reify ::modify-handler
|
(ptk/reify ::modify-handler
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
|
||||||
(let [content (get-in state (st/get-path state :content))
|
(let [content (get-in state (st/get-path state :content))
|
||||||
|
|
||||||
|
modifiers (helpers/move-handler-modifiers content index prefix false match-opposite? dx dy)
|
||||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||||
[ocx ocy] (if (= prefix :c1) [:c2x :c2y] [:c1x :c1y])
|
|
||||||
point (gpt/point (+ (get-in content [index :params cx]) dx)
|
point (gpt/point (+ (get-in content [index :params cx]) dx)
|
||||||
(+ (get-in content [index :params cy]) dy))
|
(+ (get-in content [index :params cy]) dy))
|
||||||
opposite-index (upc/opposite-index content index prefix)]
|
|
||||||
(cond-> state
|
|
||||||
:always
|
|
||||||
(-> (update-in [:workspace-local :edit-path id :content-modifiers index] assoc
|
|
||||||
cx dx cy dy)
|
|
||||||
(assoc-in [:workspace-local :edit-path id :moving-handler] point))
|
|
||||||
|
|
||||||
(and match-opposite? opposite-index)
|
]
|
||||||
(update-in [:workspace-local :edit-path id :content-modifiers opposite-index] assoc
|
|
||||||
ocx (- dx) ocy (- dy)))))))
|
(-> state
|
||||||
|
(update-in [:workspace-local :edit-path id :content-modifiers] merge modifiers)
|
||||||
|
(assoc-in [:workspace-local :edit-path id :moving-handler] point))))))
|
||||||
|
|
||||||
(defn apply-content-modifiers []
|
(defn apply-content-modifiers []
|
||||||
(ptk/reify ::apply-content-modifiers
|
(ptk/reify ::apply-content-modifiers
|
||||||
|
@ -174,15 +160,9 @@
|
||||||
content (get-in state (st/get-path state :content))
|
content (get-in state (st/get-path state :content))
|
||||||
points (upg/content->points content)
|
points (upg/content->points content)
|
||||||
|
|
||||||
opposite-index (upc/opposite-index content index prefix)
|
|
||||||
opposite-prefix (if (= prefix :c1) :c2 :c1)
|
|
||||||
opposite-handler (-> content (get opposite-index) (upc/get-handler opposite-prefix))
|
|
||||||
|
|
||||||
point (-> content (get (if (= prefix :c1) (dec index) index)) (upc/command->point))
|
point (-> content (get (if (= prefix :c1) (dec index) index)) (upc/command->point))
|
||||||
handler (-> content (get index) (upc/get-handler prefix))
|
handler (-> content (get index) (upc/get-handler prefix))
|
||||||
|
|
||||||
current-distance (when opposite-handler (gpt/distance (upg/opposite-handler point handler) opposite-handler))
|
|
||||||
match-opposite? (and opposite-handler (mth/almost-zero? current-distance))
|
|
||||||
snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])]
|
snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled])]
|
||||||
|
|
||||||
(streams/drag-stream
|
(streams/drag-stream
|
||||||
|
@ -199,7 +179,7 @@
|
||||||
prefix
|
prefix
|
||||||
(+ start-delta-x (- (:x pos) (:x start-point)))
|
(+ start-delta-x (- (:x pos) (:x start-point)))
|
||||||
(+ start-delta-y (- (:y pos) (:y start-point)))
|
(+ start-delta-y (- (:y pos) (:y start-point)))
|
||||||
(and (not alt?) match-opposite?))))))
|
(not alt?))))))
|
||||||
(rx/concat (rx/of (apply-content-modifiers)))))))))
|
(rx/concat (rx/of (apply-content-modifiers)))))))))
|
||||||
|
|
||||||
(declare stop-path-edit)
|
(declare stop-path-edit)
|
||||||
|
|
|
@ -106,15 +106,88 @@
|
||||||
(update :content (fnil conj []) command)
|
(update :content (fnil conj []) command)
|
||||||
(update-selrect))))
|
(update-selrect))))
|
||||||
|
|
||||||
(defn move-handler-modifiers
|
(defn prefix->coords [prefix]
|
||||||
[content index prefix match-opposite? dx dy]
|
(case prefix
|
||||||
(let [[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
:c1 [:c1x :c1y]
|
||||||
[ocx ocy] (if (= prefix :c1) [:c2x :c2y] [:c1x :c1y])
|
:c2 [:c2x :c2y]
|
||||||
opposite-index (upc/opposite-index content index prefix)]
|
nil))
|
||||||
|
|
||||||
(cond-> {}
|
(defn handler->point [content index prefix]
|
||||||
:always
|
(when (and (some? index)
|
||||||
|
(some? prefix)
|
||||||
|
(contains? content index))
|
||||||
|
(let [[cx cy :as coords] (prefix->coords prefix)]
|
||||||
|
(if (= :curve-to (get-in content [index :command]))
|
||||||
|
(gpt/point (get-in content [index :params cx])
|
||||||
|
(get-in content [index :params cy]))
|
||||||
|
|
||||||
|
(gpt/point (get-in content [index :params :x])
|
||||||
|
(get-in content [index :params :y]))))))
|
||||||
|
|
||||||
|
(defn handler->node [content index prefix]
|
||||||
|
(if (= prefix :c1)
|
||||||
|
(upc/command->point (get content (dec index)))
|
||||||
|
(upc/command->point (get content index))))
|
||||||
|
|
||||||
|
(defn angle-points [common p1 p2]
|
||||||
|
(mth/abs
|
||||||
|
(gpt/angle-with-other
|
||||||
|
(gpt/to-vec common p1)
|
||||||
|
(gpt/to-vec common p2))))
|
||||||
|
|
||||||
|
(defn calculate-opposite-delta [node handler opposite match-angle? match-distance? dx dy]
|
||||||
|
(when (and (some? handler) (some? opposite))
|
||||||
|
(let [;; To match the angle, the angle should be matching (angle between points 180deg)
|
||||||
|
angle-handlers (angle-points node handler opposite)
|
||||||
|
|
||||||
|
match-angle? (and match-angle? (<= (mth/abs (- 180 angle-handlers) ) 0.1))
|
||||||
|
|
||||||
|
;; To match distance the distance should be matching
|
||||||
|
match-distance? (and match-distance? (mth/almost-zero? (- (gpt/distance node handler)
|
||||||
|
(gpt/distance node opposite))))
|
||||||
|
|
||||||
|
new-handler (-> handler (update :x + dx) (update :y + dy))
|
||||||
|
|
||||||
|
v1 (gpt/to-vec node handler)
|
||||||
|
v2 (gpt/to-vec node new-handler)
|
||||||
|
|
||||||
|
delta-angle (gpt/angle-with-other v1 v2)
|
||||||
|
delta-sign (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1)
|
||||||
|
|
||||||
|
distance-scale (/ (gpt/distance node handler)
|
||||||
|
(gpt/distance node new-handler))
|
||||||
|
|
||||||
|
new-opposite (cond-> opposite
|
||||||
|
match-angle?
|
||||||
|
(gpt/rotate node (* delta-sign delta-angle))
|
||||||
|
|
||||||
|
match-distance?
|
||||||
|
(gpt/scale-from node distance-scale))]
|
||||||
|
[(- (:x new-opposite) (:x opposite))
|
||||||
|
(- (:y new-opposite) (:y opposite))])))
|
||||||
|
|
||||||
|
(defn move-handler-modifiers
|
||||||
|
[content index prefix match-distance? match-angle? dx dy]
|
||||||
|
|
||||||
|
(let [[cx cy] (prefix->coords prefix)
|
||||||
|
[op-idx op-prefix] (upc/opposite-index content index prefix)
|
||||||
|
|
||||||
|
node (handler->node content index prefix)
|
||||||
|
handler (handler->point content index prefix)
|
||||||
|
opposite (handler->point content op-idx op-prefix)
|
||||||
|
|
||||||
|
[ocx ocy] (prefix->coords op-prefix)
|
||||||
|
[odx ody] (calculate-opposite-delta node handler opposite match-angle? match-distance? dx dy)
|
||||||
|
|
||||||
|
hnv (if (some? handler)
|
||||||
|
(gpt/to-vec node (-> handler (update :x + dx) (update :y + dy)))
|
||||||
|
(gpt/point dx dy))]
|
||||||
|
|
||||||
|
(-> {}
|
||||||
(update index assoc cx dx cy dy)
|
(update index assoc cx dx cy dy)
|
||||||
|
|
||||||
(and match-opposite? opposite-index)
|
(cond-> (and (some? op-idx) (not= opposite node))
|
||||||
(update opposite-index assoc ocx (- dx) ocy (- dy)))))
|
(update op-idx assoc ocx odx ocy ody)
|
||||||
|
|
||||||
|
(and (some? op-idx) (= opposite node) match-distance? match-angle?)
|
||||||
|
(update op-idx assoc ocx (- (:x hnv)) ocy (- (:y hnv)))))))
|
||||||
|
|
|
@ -88,7 +88,9 @@
|
||||||
(gpt/add position snap))
|
(gpt/add position snap))
|
||||||
position))]
|
position))]
|
||||||
(->> ms/mouse-position
|
(->> ms/mouse-position
|
||||||
(rx/map check-path-snap))))
|
(rx/map check-path-snap)
|
||||||
|
(rx/with-latest merge (->> ms/mouse-position-shift (rx/map #(hash-map :shift? %))))
|
||||||
|
(rx/with-latest merge (->> ms/mouse-position-alt (rx/map #(hash-map :alt? %)))))))
|
||||||
|
|
||||||
(defn position-stream
|
(defn position-stream
|
||||||
[snap-toggled points]
|
[snap-toggled points]
|
||||||
|
|
|
@ -39,4 +39,3 @@
|
||||||
:base-props props
|
:base-props props
|
||||||
:elem-name "path"}])))
|
:elem-name "path"}])))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,22 +53,43 @@
|
||||||
:c2x (:x h2)
|
:c2x (:x h2)
|
||||||
:c2y (:y h2)}))
|
:c2y (:y h2)}))
|
||||||
|
|
||||||
(defn make-curve-to [to h1 h2]
|
(defn update-curve-to
|
||||||
|
[command h1 h2]
|
||||||
|
(-> command
|
||||||
|
(assoc :command :curve-to)
|
||||||
|
(assoc-in [:params :c1x] (:x h1))
|
||||||
|
(assoc-in [:params :c1y] (:y h1))
|
||||||
|
(assoc-in [:params :c2x] (:x h2))
|
||||||
|
(assoc-in [:params :c2y] (:y h2))))
|
||||||
|
|
||||||
|
(defn make-curve-to
|
||||||
|
[to h1 h2]
|
||||||
{:command :curve-to
|
{:command :curve-to
|
||||||
:relative false
|
:relative false
|
||||||
:params (make-curve-params to h1 h2)})
|
:params (make-curve-params to h1 h2)})
|
||||||
|
|
||||||
(defn apply-content-modifiers [content modifiers]
|
(defn update-handler
|
||||||
|
[command prefix point]
|
||||||
|
(let [[cox coy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])]
|
||||||
|
(-> command
|
||||||
|
(assoc-in [:params cox] (:x point))
|
||||||
|
(assoc-in [:params coy] (:y point)))))
|
||||||
|
|
||||||
|
(defn apply-content-modifiers
|
||||||
|
"Apply to content a map with point translations"
|
||||||
|
[content modifiers]
|
||||||
(letfn [(apply-to-index [content [index params]]
|
(letfn [(apply-to-index [content [index params]]
|
||||||
(if (contains? content index)
|
(if (contains? content index)
|
||||||
(cond-> content
|
(cond-> content
|
||||||
(and
|
(and
|
||||||
(or (:c1x params) (:c1y params) (:c2x params) (:c2y params))
|
(or (:c1x params) (:c1y params) (:c2x params) (:c2y params))
|
||||||
(= :line-to (get-in content [index :params :command])))
|
(= :line-to (get-in content [index :command])))
|
||||||
|
|
||||||
(-> (assoc-in [index :command] :curve-to)
|
(-> (assoc-in [index :command] :curve-to)
|
||||||
(assoc-in [index :params] :curve-to) (make-curve-params
|
(assoc-in [index :params]
|
||||||
|
(make-curve-params
|
||||||
(get-in content [index :params])
|
(get-in content [index :params])
|
||||||
(get-in content [(dec index) :params])))
|
(get-in content [(dec index) :params]))))
|
||||||
|
|
||||||
(:x params) (update-in [index :params :x] + (:x params))
|
(:x params) (update-in [index :params :x] + (:x params))
|
||||||
(:y params) (update-in [index :params :y] + (:y params))
|
(:y params) (update-in [index :params :y] + (:y params))
|
||||||
|
@ -117,6 +138,7 @@
|
||||||
(mapv (fn [[index _]] index))))
|
(mapv (fn [[index _]] index))))
|
||||||
|
|
||||||
(defn handler-indices
|
(defn handler-indices
|
||||||
|
"Return an index where the key is the positions and the values the handlers"
|
||||||
[content point]
|
[content point]
|
||||||
(->> (d/with-prev content)
|
(->> (d/with-prev content)
|
||||||
(d/enumerate)
|
(d/enumerate)
|
||||||
|
@ -132,16 +154,24 @@
|
||||||
(defn opposite-index
|
(defn opposite-index
|
||||||
"Calculate sthe opposite index given a prefix and an index"
|
"Calculate sthe opposite index given a prefix and an index"
|
||||||
[content index prefix]
|
[content index prefix]
|
||||||
|
|
||||||
(let [point (if (= prefix :c2)
|
(let [point (if (= prefix :c2)
|
||||||
(command->point (nth content index))
|
(command->point (nth content index))
|
||||||
(command->point (nth content (dec index))))
|
(command->point (nth content (dec index))))
|
||||||
|
|
||||||
handlers (-> (content->handlers content)
|
point->handlers (content->handlers content)
|
||||||
(get point))
|
|
||||||
|
|
||||||
opposite-prefix (if (= prefix :c1) :c2 :c1)]
|
handlers (->> point
|
||||||
(when (<= (count handlers) 2)
|
(point->handlers )
|
||||||
|
(filter (fn [[ci cp]] (and (not= index ci) (not= prefix cp)) )))]
|
||||||
|
|
||||||
|
(when (= (count handlers) 1)
|
||||||
(->> handlers
|
(->> handlers
|
||||||
(d/seek (fn [[index prefix]] (= prefix opposite-prefix)))
|
first))))
|
||||||
(first)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-commands
|
||||||
|
"Returns the commands involving a point with its indices"
|
||||||
|
[content point]
|
||||||
|
(->> (d/enumerate content)
|
||||||
|
(filterv (fn [[idx cmd]] (= (command->point cmd) point)))))
|
||||||
|
|
|
@ -54,59 +54,113 @@
|
||||||
(reduce change-content $ handlers)
|
(reduce change-content $ handlers)
|
||||||
(remove-line-curves $))))
|
(remove-line-curves $))))
|
||||||
|
|
||||||
|
(defn line->curve
|
||||||
|
[from-p cmd]
|
||||||
|
|
||||||
|
(let [to-p (upc/command->point cmd)
|
||||||
|
|
||||||
|
v (gpt/to-vec from-p to-p)
|
||||||
|
d (gpt/distance from-p to-p)
|
||||||
|
|
||||||
|
dv1 (-> (gpt/normal-left v)
|
||||||
|
(gpt/scale (/ d 3)))
|
||||||
|
|
||||||
|
h1 (gpt/add from-p dv1)
|
||||||
|
|
||||||
|
dv2 (-> (gpt/to-vec to-p h1)
|
||||||
|
(gpt/unit)
|
||||||
|
(gpt/scale (/ d 3)))
|
||||||
|
|
||||||
|
h2 (gpt/add to-p dv2)]
|
||||||
|
(-> cmd
|
||||||
|
(assoc :command :curve-to)
|
||||||
|
(assoc-in [:params :c1x] (:x h1))
|
||||||
|
(assoc-in [:params :c1y] (:y h1))
|
||||||
|
(assoc-in [:params :c2x] (:x h2))
|
||||||
|
(assoc-in [:params :c2y] (:y h2)))))
|
||||||
|
|
||||||
(defn make-curve-point
|
(defn make-curve-point
|
||||||
"Changes the content to make the point a 'curve'. The handlers will be positioned
|
"Changes the content to make the point a 'curve'. The handlers will be positioned
|
||||||
in the same vector that results from te previous->next points but with fixed length."
|
in the same vector that results from te previous->next points but with fixed length."
|
||||||
[content point]
|
[content point]
|
||||||
(let [content-next (d/enumerate (d/with-prev-next content))
|
|
||||||
|
|
||||||
make-curve
|
(let [make-curve-cmd (fn [cmd h1 h2]
|
||||||
(fn [command previous]
|
(-> cmd
|
||||||
(if (= :line-to (:command command))
|
(update :params assoc
|
||||||
(let [cur-point (upc/command->point command)
|
:c1x (:x h1) :c1y (:y h1)
|
||||||
pre-point (upc/command->point previous)]
|
:c2x (:x h2) :c2y (:y h2))))
|
||||||
(-> command
|
|
||||||
(assoc :command :curve-to)
|
|
||||||
(assoc :params (upc/make-curve-params cur-point pre-point))))
|
|
||||||
command))
|
|
||||||
|
|
||||||
update-handler
|
indices (upc/point-indices content point)
|
||||||
(fn [command prefix handler]
|
vectors (->> indices (mapv (fn [index]
|
||||||
(if (= :curve-to (:command command))
|
(let [cmd (nth content index)
|
||||||
(let [cx (d/prefix-keyword prefix :x)
|
prev-i (dec index)
|
||||||
cy (d/prefix-keyword prefix :y)]
|
prev (when (not (= :move-to (:command cmd)))
|
||||||
(-> command
|
(get content prev-i))
|
||||||
(assoc-in [:params cx] (:x handler))
|
next-i (inc index)
|
||||||
(assoc-in [:params cy] (:y handler))))
|
next (get content next-i)
|
||||||
command))
|
|
||||||
|
|
||||||
calculate-vector
|
next (when (not (= :move-to (:command next)))
|
||||||
(fn [point next prev]
|
next)]
|
||||||
(let [base-vector (if (or (nil? next) (nil? prev) (= next prev))
|
(hash-map :index index
|
||||||
(-> (gpt/to-vec point (or next prev))
|
:prev-i (when (some? prev) prev-i)
|
||||||
(gpt/normal-left))
|
:prev-c prev
|
||||||
(gpt/to-vec next prev))]
|
:prev-p (upc/command->point prev)
|
||||||
(-> base-vector
|
:next-i (when (some? next) next-i)
|
||||||
(gpt/unit)
|
:next-c next
|
||||||
(gpt/multiply (gpt/point 100)))))
|
:next-p (upc/command->point next)
|
||||||
|
:command cmd)))))
|
||||||
|
|
||||||
redfn (fn [content [index [command prev next]]]
|
|
||||||
(if (= point (upc/command->point command))
|
|
||||||
(let [prev-point (if (= :move-to (:command command)) nil (upc/command->point prev))
|
|
||||||
next-point (if (= :move-to (:command next)) nil (upc/command->point next))
|
|
||||||
handler-vector (calculate-vector point next-point prev-point)
|
|
||||||
handler (gpt/add point handler-vector)
|
|
||||||
handler-opposite (gpt/add point (gpt/negate handler-vector))]
|
|
||||||
(-> content
|
|
||||||
(d/update-when index make-curve prev)
|
|
||||||
(d/update-when index update-handler :c2 handler)
|
|
||||||
(d/update-when (inc index) make-curve command)
|
|
||||||
(d/update-when (inc index) update-handler :c1 handler-opposite)))
|
|
||||||
|
|
||||||
content))]
|
points (->> vectors (mapcat #(vector (:next-p %) (:prev-p %))) (remove nil?) (into #{}))]
|
||||||
(as-> content $
|
|
||||||
(reduce redfn $ content-next)
|
(cond
|
||||||
(remove-line-curves $))))
|
(= (count points) 2)
|
||||||
|
;;
|
||||||
|
(let [v1 (gpt/to-vec (first points) point)
|
||||||
|
v2 (gpt/to-vec (first points) (second points))
|
||||||
|
vp (gpt/project v1 v2)
|
||||||
|
vh (gpt/subtract v1 vp)
|
||||||
|
|
||||||
|
add-curve
|
||||||
|
(fn [content {:keys [index prev-p next-p next-i]}]
|
||||||
|
(let [cur-cmd (get content index)
|
||||||
|
next-cmd (get content next-i)
|
||||||
|
|
||||||
|
;; New handlers for prev-point and next-point
|
||||||
|
prev-h (when (some? prev-p) (gpt/add prev-p vh))
|
||||||
|
next-h (when (some? next-p) (gpt/add next-p vh))
|
||||||
|
|
||||||
|
;; Correct 1/3 to the point improves the curve
|
||||||
|
prev-correction (when (some? prev-h) (gpt/scale (gpt/to-vec prev-h point) (/ 1 3)))
|
||||||
|
next-correction (when (some? next-h) (gpt/scale (gpt/to-vec next-h point) (/ 1 3)))
|
||||||
|
|
||||||
|
prev-h (when (some? prev-h) (gpt/add prev-h prev-correction))
|
||||||
|
next-h (when (some? next-h) (gpt/add next-h next-correction))
|
||||||
|
]
|
||||||
|
(cond-> content
|
||||||
|
(and (= :line-to (:command cur-cmd)) (some? prev-p))
|
||||||
|
(update index upc/update-curve-to prev-p prev-h)
|
||||||
|
|
||||||
|
(and (= :line-to (:command next-cmd)) (some? next-p))
|
||||||
|
(update next-i upc/update-curve-to next-h next-p)
|
||||||
|
|
||||||
|
(and (= :curve-to (:command cur-cmd)) (some? prev-p))
|
||||||
|
(update index upc/update-handler :c2 prev-h)
|
||||||
|
|
||||||
|
(and (= :curve-to (:command next-cmd)) (some? next-p))
|
||||||
|
(update next-i upc/update-handler :c1 next-h))))]
|
||||||
|
(->> vectors (reduce add-curve content)))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [add-curve
|
||||||
|
(fn [content {:keys [index command prev-p next-c next-i]}]
|
||||||
|
(cond-> content
|
||||||
|
(and (= :line-to (:command command)))
|
||||||
|
(update index #(line->curve prev-p %))
|
||||||
|
|
||||||
|
(and (= :line-to (:command next-c)))
|
||||||
|
(update next-i #(line->curve point %))))]
|
||||||
|
(->> vectors (reduce add-curve content))))))
|
||||||
|
|
||||||
(defn get-segments
|
(defn get-segments
|
||||||
"Given a content and a set of points return all the segments in the path
|
"Given a content and a set of points return all the segments in the path
|
||||||
|
|
Loading…
Add table
Reference in a new issue