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:
parent
1af87b9140
commit
22975f4f7d
14 changed files with 295 additions and 360 deletions
|
@ -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]
|
||||
|
|
|
@ -30,6 +30,4 @@
|
|||
|
||||
.dashboard-content {
|
||||
background-color: lighten($color-gray-10, 5%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}]]]
|
||||
|
||||
|
|
|
@ -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}]))
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)}]
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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))))
|
||||
|
||||
|
|
|
@ -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."
|
||||
|
|
Loading…
Add table
Reference in a new issue