0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 23:49:45 -05:00

More work on viewpoer sizing handling.

This commit is contained in:
Andrey Antukh 2020-05-11 08:06:41 +02:00 committed by Alonso Torres
parent 1af87b9140
commit 22975f4f7d
14 changed files with 295 additions and 360 deletions

View file

@ -5,7 +5,7 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2019-2020 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.common.pages
"A common (clj/cljs) functions and specs for pages."
@ -80,22 +80,25 @@
(remove p? after))))
(defn select-toplevel-shapes
[objects]
(let [lookup #(get objects %)
root (lookup uuid/zero)
childs (:shapes root)]
(loop [id (first childs)
ids (rest childs)
res []]
(if (nil? id)
res
(let [obj (lookup id)
typ (:type obj)]
(recur (first ids)
(rest ids)
(if (= :frame typ)
(into res (map lookup) (:shapes obj))
(conj res obj))))))))
([objects] (select-toplevel-shapes objects nil))
([objects {:keys [include-frames?] :or {include-frames? false}}]
(let [lookup #(get objects %)
root (lookup uuid/zero)
childs (:shapes root)]
(loop [id (first childs)
ids (rest childs)
res []]
(if (nil? id)
res
(let [obj (lookup id)
typ (:type obj)]
(recur (first ids)
(rest ids)
(if (= :frame typ)
(if include-frames?
(d/concat res [obj] (map lookup (:shapes obj)))
(d/concat res (map lookup (:shapes obj))))
(conj res obj)))))))))
(defn select-frames
[objects]

View file

@ -30,6 +30,4 @@
.dashboard-content {
background-color: lighten($color-gray-10, 5%);
display: flex;
flex-direction: column;
}

View file

@ -119,7 +119,7 @@
}
.workspace-viewport {
height: 100%;
height: calc(100% - 40px);
overflow: hidden;
transition: none;
width: 100%;
@ -129,8 +129,9 @@
grid-template-columns: 20px 1fr;
.viewport {
grid-column: 2 / span 1;
grid-row: 2 / span 1;
grid-column: 1 / span 2;
grid-row: 1 / span 2;
overflow: hidden;
&.drawing {
cursor: cell;
@ -156,7 +157,7 @@
}
}
.viewport, .page-canvas, .page-layout {
.page-canvas, .page-layout {
overflow: visible;
}
}

View file

@ -66,18 +66,13 @@
:sitemap-pages
:layers
:element-options
:rules})
:rules
})
(s/def ::options-mode #{:design :prototype})
(def workspace-default
{:zoom 1
:size {:x 0
:y 0
:width c/viewport-width
:height c/viewport-width
:viewport-width c/viewport-width
:viewport-height c/viewport-height}
:flags #{}
:selected #{}
:drawing nil
@ -196,51 +191,59 @@
;; --- Viewport Sizing
(declare zoom-to-fit-all)
;; TODO: add-spec
(defn initialize-viewport
[{:keys [width height] :as size}]
(js/console.log "initialize-viewport" size)
(ptk/reify ::initialize-viewport
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local
(fn [local]
(-> local
(assoc :zoom 1)
(update :size assoc
:x 0
:y 0
:width width
:height height
:viewport-width width
:viewport-height height)))))))
(assoc :vport size)
(update :vbox (fn [vbox]
(if (nil? vbox)
(assoc size :x 0 :y 0)
vbox)))))))
ptk/WatchEvent
(watch [_ state stream]
#_(rx/of zoom-to-fit-all))))
(defn update-viewport-position
[{:keys [x y] :or {x identity y identity}}]
(us/assert fn? x)
(us/assert fn? y)
(ptk/reify ::update-viewport-position
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-local :size]
(fn [size]
(-> size
(update :x x)
(update :y y)))))))
(update-in state [:workspace-local :vbox]
(fn [vbox]
(-> vbox
(update :x (comp mth/round x))
(update :y (comp mth/round y))))))))
;; TODO: add spec
(defn update-viewport-size
[{:keys [width height]}]
[{:keys [width height] :as size}]
(ptk/reify ::update-viewport-size
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local
(fn [{:keys [size] :as local}]
(let [wprop (/ (:viewport-width size) width)
hprop (/ (:viewport-height size) height)
size' (-> size
(assoc :viewport-width width)
(assoc :viewport-height height)
(update :width #(/ % wprop))
(update :height #(/ % hprop)))]
(assoc local :size size')))))))
(fn [{:keys [vbox vport] :as local}]
(let [wprop (/ (:width vport) width)
hprop (/ (:height vport) height)]
(-> local
(assoc :vport size)
(update :vbox (fn [vbox]
(-> vbox
(update :width #(/ % wprop))
(update :height #(/ % hprop))))))))))))
;; ---
@ -325,19 +328,15 @@
;; --- Zoom Management
(defn- impl-update-zoom
[local zoom]
(let [base-width (get-in local [:size :viewport-width])
base-height (get-in local [:size :viewport-height])
zoom (if (fn? zoom)
[{:keys [vbox vport] :as local} zoom]
(let [zoom (if (fn? zoom)
(zoom (:zoom local))
zoom)
width (/ base-width zoom)
height (/ base-height zoom)]
width (/ (:width vport) zoom)
height (/ (:height vport) zoom)]
(-> local
(assoc :zoom zoom)
(update :size assoc :width width :height height))))
(update :vbox assoc :width width :height height))))
(defn increase-zoom
[center]
@ -376,51 +375,45 @@
(update state :workspace-local
#(impl-update-zoom % 2)))))
;; (defn impl-expand-rect
;; [{:keys [x y width height] :as rect} padding]
;; (assoc rect
;; :x (- x padding)
;; :y (- y padding)
;; :width (+ width padding)
;; :height (+ height padding)))
(def zoom-to-fit-all
(ptk/reify ::zoom-to-fit-all
ptk/UpdateEvent
(update [_ state]
(let [page-id (get-in state [:workspace-page :id])
objects (get-in state [:workspace-data page-id :objects])
shapes (cp/select-toplevel-shapes objects {:include-frames? true})
srect (geom/shapes->rect-shape shapes)]
;; (def zoom-to-selected-shape
;; (ptk/reify ::zoom-to-selected-shape
;; ptk/UpdateEvent
;; (update [_ state]
;; (let [page-id (get-in state [:workspace-page :id])
;; objects (get-in state [:workspace-data page-id :objects])
;; selected (get-in state [:workspace-local :selected])
;; shapes (map #(get objects %) selected)
;; rect (geom/selection-rect shapes)
;; rect (impl-expand-rect rect 20)
;; ]
;; (update state :workspace-local
;; (fn [{:keys [size] :as local}]
;; (let [proportion (/ (:viewport-width size) (:viewport-height size))
;; width (:width rect)
;; height (/ (:width rect) proportion)
;; [width height] (if (> (:height rect) height)
;; [(* width proportion) (:height rect)]
;; [width height])
;; zoom (/ (:viewport-width size) width)]
;; ;; (js/console.log "proportion=" proportion
;; ;; "width=" width
;; ;; "height=" height
;; ;; "viewport-width=" (:viewport-width size)
;; ;; "viewport-height=" (:viewport-height size))
;; (-> local
;; (assoc :zoom zoom)
;; (update :size assoc
;; :x (:x rect)
;; :y (:y rect)
;; :width width
;; :height height
;; )))))))))
(if (or (mth/nan? (:width srect))
(mth/nan? (:height srect)))
state
(update state :workspace-local
(fn [{:keys [vbox vport] :as local}]
(let [srect (geom/adjust-to-viewport vport srect {:padding 40})
zoom (/ (:width vport) (:width srect))]
(-> local
(assoc :zoom zoom)
(update :vbox merge srect))))))))))
(def zoom-to-selected-shape
(ptk/reify ::zoom-to-selected-shape
ptk/UpdateEvent
(update [_ state]
(let [selected (get-in state [:workspace-local :selected])]
(if (empty? selected)
state
(let [page-id (get-in state [:workspace-page :id])
objects (get-in state [:workspace-data page-id :objects])
srect (->> selected
(map #(get objects %))
(geom/selection-rect))]
(update state :workspace-local
(fn [{:keys [vbox vport] :as local}]
(let [srect (geom/adjust-to-viewport vport srect {:padding 40})
zoom (/ (:width vport) (:width srect))]
(-> local
(assoc :zoom zoom)
(update :vbox merge srect)))))))))))
;; --- Selection Rect
@ -1464,9 +1457,10 @@
"ctrl+g" #(st/emit! create-group)
"ctrl+shift+g" #(st/emit! remove-group)
"shift+0" #(st/emit! zoom-to-50)
"shift+1" #(st/emit! reset-zoom)
"shift+2" #(st/emit! zoom-to-200)
;; "shift+2" #(st/emit! zoom-to-selected-shape)
;; "shift+1" #(st/emit! reset-zoom)
"shift+1" #(st/emit! zoom-to-fit-all)
;; "shift+2" #(st/emit! zoom-to-200)
"shift+2" #(st/emit! zoom-to-selected-shape)
"ctrl+d" #(st/emit! duplicate-selected)
"ctrl+z" #(st/emit! dwc/undo)
"ctrl+shift+z" #(st/emit! dwc/redo)

View file

@ -1,3 +1,12 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.data.workspace.transforms
"Events related with shapes transformations"
(:require

View file

@ -2,7 +2,10 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.exports
"The main logic for SVG export functionality."
@ -34,13 +37,11 @@
:fill background-color}])
(defn- calculate-dimensions
[data]
(let [shapes (vals (:objects data))
shape (geom/shapes->rect-shape shapes)
width (+ (:x shape) (:width shape) 100)
height (+ (:y shape) (:height shape) 100)]
{:width (if (mth/nan? width) 100 width)
:height (if (mth/nan? height) 100 height)}))
[{:keys [objects] :as data} vport]
(let [shapes (cp/select-toplevel-shapes objects {:include-frames? true})]
(->> (geom/shapes->rect-shape shapes)
(geom/adjust-to-viewport vport)
(geom/fix-invalid-rect-values))))
(declare shape-wrapper-factory)
@ -86,14 +87,19 @@
(mf/defc page-svg
{::mf/wrap [mf/memo]}
[{:keys [data] :as props}]
[{:keys [data width height] :as props}]
(let [objects (:objects data)
dim (calculate-dimensions data)
vport {:width width :height height}
dim (calculate-dimensions data vport)
root (get objects uuid/zero)
shapes (->> (:shapes root)
(map #(get objects %)))
vbox (str "0 0 " (:width dim 0) " " (:height dim 0))
vbox (str (:x dim 0) " "
(:y dim 0) " "
(:width dim 100) " "
(:height dim 100))
frame-wrapper
(mf/use-memo
@ -104,7 +110,6 @@
(mf/use-memo
(mf/deps objects)
#(shape-wrapper-factory objects))]
[:svg {:view-box vbox
:version "1.1"
:xmlnsXlink "http://www.w3.org/1999/xlink"

View file

@ -61,11 +61,14 @@
[:*
[:div.empty-rule-square]
[:& horizontal-rule {:zoom (:zoom local)
:size (:size local)}]
:vbox (:vbox local)
:vport (:vport local)}]
[:& vertical-rule {:zoom (:zoom local 1)
:size (:size local)}]])
:vbox (:vbox local)
:vport (:vport local)}]])
[:& viewport {:page page
:key (:id page)
:file file
:local local}]]]

View file

@ -10,121 +10,100 @@
(ns uxbox.main.ui.workspace.rules
(:require
[rumext.alpha :as mf]
[uxbox.util.math :as mth]
[uxbox.util.object :as obj]))
(def STEP-PADDING 20)
(defn draw-rule!
[dctx {:keys [zoom size start count type] :or {count 200}}]
(let [txfm (- (* (- 0 start) zoom) 20)
minv (mth/round start)
maxv (mth/round (+ start (/ size zoom)))
step (mth/round (/ (mth/abs (- maxv minv)) count))
step (max (* 1 (* 10 step)) 1)]
(obj/set! dctx "fillStyle" "#E8E9EA")
(if (= type :horizontal)
(do
(.fillRect dctx 0 0 size 20)
(.translate dctx txfm 0))
(do
(.fillRect dctx 0 0 20 size)
(.translate dctx 0 txfm)))
(obj/set! dctx "font" "12px serif")
(obj/set! dctx "fillStyle" "#7B7D85")
(obj/set! dctx "strokeStyle" "#7B7D85")
(obj/set! dctx "textAlign" "center")
(loop [i minv]
(when (< i maxv)
(let [pos (+ (* i zoom) 0)]
(when (= (mod i step) 0)
(.save dctx)
(if (= type :horizontal)
(do
(.fillText dctx (str i) pos 13))
(do
(.translate dctx 12 pos)
(.rotate dctx (/ (* 270 js/Math.PI) 180))
(.fillText dctx (str i) 0 0)))
(.restore dctx))
(recur (inc i)))))
(let [path (js/Path2D.)]
(loop [i minv]
(if (> i maxv)
(.stroke dctx path)
(let [pos (+ (* i zoom) 0)]
(when (= (mod i step) 0)
(if (= type :horizontal)
(do
(.moveTo path pos 17)
(.lineTo path pos 20))
(do
(.moveTo path 17 pos)
(.lineTo path 20 pos))))
(recur (inc i))))))))
(mf/defc horizontal-rule
[{:keys [zoom size]}]
[{:keys [zoom vbox vport] :as props}]
(let [canvas (mf/use-ref)
{:keys [x viewport-width width]} size]
width (- (:width vport) 20)]
(mf/use-layout-effect
(mf/deps viewport-width width x zoom)
(mf/deps zoom width (:x vbox))
(fn []
(let [node (mf/ref-val canvas)
dctx (.getContext node "2d")
dctx (.getContext ^js node "2d")]
(obj/set! node "width" width)
(draw-rule! dctx {:zoom zoom
:type :horizontal
:size width
:start (:x vbox)}))))
btm 1
trx (- (* (- 0 x) zoom) 50)
min-val (js/Math.round x)
max-val (js/Math.round (+ x (/ viewport-width zoom)))
tmp0 (js/Math.abs (- max-val min-val))
tmp1 (js/Math.round (/ tmp0 200))
btm (max (* btm (* 10 tmp1)) 1)]
(obj/set! node "width" viewport-width)
(obj/set! dctx "fillStyle" "#E8E9EA")
(.fillRect dctx 0 0 viewport-width 20)
(.save dctx)
(.translate dctx trx 0)
(obj/set! dctx "font" "12px serif")
(obj/set! dctx "fillStyle" "#7B7D85")
(obj/set! dctx "strokeStyle" "#7B7D85")
(obj/set! dctx "textAlign" "center")
(loop [i min-val]
(when (< i max-val)
(let [pos (+ (* i zoom) 50)]
(when (= (mod i btm) 0)
(.fillText dctx (str i) (- pos 0) 13))
(recur (+ i 1)))))
(let [path (js/Path2D.)]
(loop [i min-val]
(if (> i max-val)
(.stroke dctx path)
(let [pos (+ (* i zoom) 50)]
(when (= (mod i btm) 0)
(.moveTo path pos 17)
(.lineTo path pos STEP-PADDING))
(recur (inc i))))))
(.restore dctx))))
[:canvas.horizontal-rule {:ref canvas :width (:viewport-width size) :height 20}]))
;; --- Vertical Rule (Component)
[:canvas.horizontal-rule
{:ref canvas
:width width
:height 20}]))
(mf/defc vertical-rule
{::mf/wrap [mf/memo #(mf/throttle % 60)]}
[{:keys [zoom size]}]
[{:keys [zoom vbox vport] :as props}]
(let [canvas (mf/use-ref)
{:keys [y height viewport-height]} size]
height (- (:height vport) 20)]
(mf/use-layout-effect
(mf/deps height y zoom)
(mf/deps zoom height (:y vbox))
(fn []
(let [node (mf/ref-val canvas)
dctx (.getContext node "2d")
btm 1
try (- (* (- 0 y) zoom) 50)
min-val (js/Math.round y)
max-val (js/Math.round (+ y (/ viewport-height zoom)))
tmp0 (js/Math.abs (- max-val min-val))
tmp1 (js/Math.round (/ tmp0 100))
btm (max (* btm (* 10 tmp1)) 1)]
(obj/set! node "height" viewport-height)
(obj/set! dctx "fillStyle" "#E8E9EA")
(.fillRect dctx 0 0 20 viewport-height)
(obj/set! dctx "font" "11px serif")
(obj/set! dctx "fillStyle" "#7B7D85")
(obj/set! dctx "strokeStyle" "#7B7D85")
(obj/set! dctx "textAlign" "center")
(.translate dctx 0 try)
(loop [i min-val]
(when (< i max-val)
(let [pos (+ (* i zoom) 50)]
(when (= (mod i btm) 0)
(.save dctx)
(.translate dctx 12 pos)
(.rotate dctx (/ (* 270 js/Math.PI) 180))
(.fillText dctx (str i) 0 0)
(.restore dctx))
(recur (inc i)))))
(let [path (js/Path2D.)]
(loop [i min-val]
(if (> i max-val)
(.stroke dctx path)
(let [pos (+ (* i zoom) 50)]
(when (= (mod i btm) 0)
(.moveTo path 17 pos)
(.lineTo path STEP-PADDING pos))
(recur (inc i)))))))))
[:canvas.vertical-rule {:ref canvas :width 20 :height height}]))
dctx (.getContext ^js node "2d")]
(obj/set! node "height" height)
(draw-rule! dctx {:zoom zoom
:type :vertical
:size height
:count 100
:start (:y vbox)}))))
[:canvas.vertical-rule
{:ref canvas
:width 20
:height height}]))

View file

@ -118,7 +118,7 @@
:width (+ width 2)
:height (+ height 2)
:style {:stroke "#1FDEA7"
:stroke-width "1"
:stroke-width (/ 1 zoom)
:fill "transparent"}}])
(when (not (#{:move :rotate} current-transform))

View file

@ -98,7 +98,8 @@
:transform (str
"scale(" inv-zoom ", " inv-zoom ") "
"translate(" (* zoom (:x label-pos)) ", "
(* zoom (:y label-pos)) ")")
(* zoom (:y label-pos))
")")
;; User may also select the frame with single click in the label
:on-click on-double-click}
(:name shape)]

View file

@ -130,7 +130,8 @@
{:keys [drawing-tool
zoom
flags
size
vport
vbox
edition
tooltip
selected]} local
@ -276,7 +277,8 @@
(st/emit! (dw/decrease-zoom pos))
(st/emit! (dw/increase-zoom pos))))
(let [event (.getBrowserEvent event)
delta (.-deltaY ^js event)]
delta (.-deltaY ^js event)
delta (/ delta @refs/selected-zoom)]
(if (kbd/shift? event)
(st/emit! (dw/update-viewport-position {:x #(+ % delta)}))
(st/emit! (dw/update-viewport-position {:y #(+ % delta)})))))))
@ -299,28 +301,23 @@
on-resize
(fn [event]
(let [node (mf/ref-val viewport-ref)
parent (.-parentElement ^js node)]
(st/emit! (dw/update-viewport-size
{:width (.-clientWidth ^js parent)
:height (.-clientHeight ^js parent)}))))
prnt (dom/get-parent node)]
(st/emit! (dw/update-viewport-size (dom/get-client-size prnt)))))
on-mount
(fn []
(let [node (mf/ref-val viewport-ref)
prnt (.-parentElement ^js node)
prnt (dom/get-parent node)
key1 (events/listen js/document EventType.KEYDOWN on-key-down)
key2 (events/listen js/document EventType.KEYUP on-key-up)
key3 (events/listen node EventType.MOUSEMOVE on-mouse-move)
;; bind with passive=false to allow the event to be cancelled
;; https://stackoverflow.com/a/57582286/3219895
key4 (events/listen js/window EventType.WHEEL on-mouse-wheel
#js {"passive" false})
key4 (events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false})
key5 (events/listen js/window EventType.RESIZE on-resize)]
(st/emit! (dw/initialize-viewport
{:width (.-clientWidth ^js prnt)
:height (.-clientHeight ^js prnt)}))
(st/emit! (dw/initialize-viewport (dom/get-client-size prnt)))
(fn []
(events/unlistenByKey key1)
@ -335,22 +332,23 @@
(mf/use-effect on-mount)
[:*
[:& coordinates {:zoom zoom}]
[:svg.viewport {
:width (:viewport-width size)
:height (:viewport-height size)
:view-box (str/join " " [(:x size)
(:y size)
(:width size)
(:height size)])
:ref viewport-ref
:class (when drawing-tool "drawing")
:on-context-menu on-context-menu
:on-click on-click
:on-double-click on-double-click
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up
:on-drag-over on-drag-over
:on-drop on-drop}
[:svg.viewport
{:preserveAspectRatio "xMidYMid meet"
:width (:width vport 0)
:height (:height vport 0)
:view-box (str/join " " [(:x vbox 0)
(:y vbox 0)
(:width vbox 0 )
(:height vbox 0)])
:ref viewport-ref
:class (when drawing-tool "drawing")
:on-context-menu on-context-menu
:on-click on-click
:on-double-click on-double-click
:on-mouse-down on-mouse-down
:on-mouse-up on-mouse-up
:on-drag-over on-drag-over
:on-drop on-drop}
[:g
[:& frames {:key (:id page)}]

View file

@ -5,8 +5,7 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.util.dom
(:require
@ -69,6 +68,10 @@
[event]
(.-target event))
(defn get-parent
[dom]
(.-parentElement ^js dom))
(defn get-value
"Extract the value from dom node."
[node]
@ -150,6 +153,11 @@
y (.-clientY event)]
(gpt/point x y)))
(defn get-client-size
[node]
{:width (.-clientWidth ^js node)
:height (.-clientHeight ^js node)})
(defn focus!
[node]
(.focus node))

View file

@ -2,6 +2,9 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.util.geom.shapes
@ -80,52 +83,6 @@
dy (if y (- (_chk y) (_chk (:y shape))) 0)]
(move shape (gpt/point dx dy))))
;; --- Rotation
;; TODO: maybe we can consider apply the rotation
;; directly to the shape coordinates?
;; FIXME: deprecated, should be removed
(defn rotate
"Apply the rotation to the shape."
[shape rotation]
(assoc shape :rotation rotation))
;; --- Corner points
(defn corner-points [points]
(let [minx (apply min (map :x points))
miny (apply min (map :y points))
maxx (apply max (map :x points))
maxy (apply max (map :y points))]
{:x1 minx :y1 miny :x2 maxx :y2 maxy}))
;; --- Size
(declare size-path)
(defn size
"Calculate the size of the shape."
[shape]
(case (:type shape)
:curve (size-path shape)
:path (size-path shape)
shape))
(defn- size-path
[{:keys [segments x1 y1 x2 y2] :as shape}]
(if (and x1 y1 x2 y2)
(assoc shape
:width (- x2 x1)
:height (- y2 y1))
(let [minx (apply min (map :x segments))
miny (apply min (map :y segments))
maxx (apply max (map :x segments))
maxy (apply max (map :y segments))]
(assoc shape
:width (- maxx minx)
:height (- maxy miny)))))
;; --- Center
(declare center-rect)
@ -174,8 +131,6 @@
[{:keys [width height] :as shape}]
(assoc shape :proportion (/ width height)))
;; TODO: implement the rest of shapes
;; --- Paths
(defn update-path-point
@ -231,49 +186,6 @@
(assoc :height value)
(assoc :width (* value proportion)))))))
;; --- Resize
(defn calculate-scale-ratio
"Calculate the scale factor from one shape to an other.
The shapes should be of rect-like type because width
and height are used for calculate the ratio."
[origin final]
[(/ (:width final) (:width origin))
(/ (:height final) (:height origin))])
(defn- get-vid-coords [vid]
(case vid
:top-left [:x2 :y2]
:top-right [:x1 :y2]
:top [:x1 :y2]
:bottom-left [:x2 :y1]
:bottom-right [:x :y ]
:bottom [:x1 :y1]
:right [:x1 :y1]
:left [:x2 :y1]))
(defn resize-shape
"Apply a resize transformation to a rect-like shape. The shape
should have the `width` and `height` attrs, because these attrs
are used for the resize transformation.
Mainly used in drawarea and interactive resize on workspace
with the main objective that on the end of resize have a way
a calculte the resize ratio with `calculate-scale-ratio`."
[vid shape initial target lock?]
(let [{:keys [x y]} (gpt/subtract target initial)
[cor-x cor-y] (get-vid-coords vid)]
(let [final-x (if (#{:top :bottom} vid) (:x2 shape) x)
final-y (if (#{:right :left} vid) (:y2 shape) y)
width (Math/abs (- final-x (cor-x shape)))
height (Math/abs (- final-y (cor-y shape)))
proportion (:proportion shape 1)]
(assoc shape
:width width
:height (if lock? (/ width proportion) height)))))
;; --- Setup (Initialize)
(declare setup-rect)
@ -454,15 +366,6 @@
;; --- Outer Rect
(defn rotation-matrix
"Generate a rotation matrix from shape."
[{:keys [x y width height rotation] :as shape}]
(let [cx (+ x (/ width 2))
cy (+ y (/ height 2))]
(cond-> (gmt/matrix)
(and rotation (pos? rotation))
(gmt/rotate rotation (gpt/point cx cy)))))
(declare transform-apply-modifiers)
(defn selection-rect-shape
@ -730,7 +633,8 @@
(gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec))
(gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim)))))
(defn- fix-invalid-rect-values [rect-shape]
(defn fix-invalid-rect-values
[rect-shape]
(letfn [(check [num] (if (or (nil? num) (mth/nan? num)) 0 num))
(to-positive [num] (if (< num 1) 1 num))]
(-> rect-shape
@ -831,3 +735,33 @@
(gmt/translate shape-center)
(gmt/multiply (:transform shape (gmt/matrix)))
(gmt/translate (gpt/negate shape-center))))))
(defn adjust-to-viewport
([viewport srect] (adjust-to-viewport viewport srect nil))
([viewport srect {:keys [padding] :or {padding 0}}]
(let [gprop (/ (:width viewport) (:height viewport))
srect (-> srect
(update :x #(- % padding))
(update :y #(- % padding))
(update :width #(+ % padding padding))
(update :height #(+ % padding padding)))
width (:width srect)
height (:height srect)
lprop (/ width height)]
(cond
(> gprop lprop)
(let [width' (* (/ width lprop) gprop)
padding (/ (- width' width) 2)]
(-> srect
(update :x #(- % padding))
(assoc :width width')))
(< gprop lprop)
(let [height' (/ (* height lprop) gprop)
padding (/ (- height' height) 2)]
(-> srect
(update :y #(- % padding))
(assoc :height height')))
:else srect))))

View file

@ -2,8 +2,10 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.util.math
"A collection of math utils."