♻️ Moved math and some geom namespaces to common

This commit is contained in:
alonso.torres 2020-05-21 15:27:33 +02:00
parent d21a3e7ca2
commit 7d464c14c0
60 changed files with 172 additions and 761 deletions

View file

@ -29,10 +29,10 @@
[uxbox.main.store :as st]
[uxbox.main.streams :as ms]
[uxbox.main.worker :as uw]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]
[uxbox.util.math :as mth]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.math :as mth]
[uxbox.util.router :as rt]
[uxbox.util.transit :as t]
[uxbox.util.webapi :as wapi]))

View file

@ -19,7 +19,7 @@
[uxbox.common.uuid :as uuid]
[uxbox.main.worker :as uw]
[uxbox.util.timers :as ts]
[uxbox.util.geom.shapes :as geom]))
[uxbox.common.geom.shapes :as geom]))
;; --- Protocols

View file

@ -21,7 +21,7 @@
[uxbox.main.data.workspace.common :as dwc]
[uxbox.main.data.workspace.persistence :as dwp]
[uxbox.util.avatars :as avatars]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.time :as dt]
[uxbox.util.transit :as t]
[uxbox.util.websockets :as ws]))

View file

@ -19,7 +19,7 @@
[uxbox.main.data.workspace.common :as dwc]
[uxbox.main.repo :as rp]
[uxbox.main.store :as st]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.router :as rt]
[uxbox.util.time :as dt]
[uxbox.util.transit :as t]))

View file

@ -1,95 +0,0 @@
;; 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/.
;;
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.data.workspace.ruler
"Workspace ruler related events. Mostly or all events
are related to UI logic."
#_(:require [beicon.core :as rx]
[potok.core :as ptk]
[uxbox.main.refs :as refs]
[uxbox.main.streams :as streams]
[uxbox.main.user-events :as uev]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]))
;; --- Constants
;; (declare stop-ruler?)
;; (declare clear-ruler)
;; (declare update-ruler)
;; (def ^:private immanted-zones
;; (let [transform #(vector (- % 7) (+ % 7) %)
;; right (map transform (range 0 181 15))
;; left (map (comp transform -) (range 0 181 15))]
;; (vec (concat right left))))
;; (defn- align-position
;; [pos]
;; (let [angle (gpt/angle pos)]
;; (reduce (fn [pos [a1 a2 v]]
;; (if (< a1 angle a2)
;; (reduced (gpt/update-angle pos v))
;; pos))
;; pos
;; immanted-zones)))
;; ;; --- Start Ruler
;; (deftype StartRuler []
;; ptk/UpdateEvent
;; (update [_ state]
;; (let [pid (get-in state [:workspace :current])
;; pos (get-in state [:workspace :pointer :viewport])]
;; (assoc-in state [:workspace pid :ruler] {:start pos :end pos})))
;; ptk/WatchEvent
;; (watch [_ state stream]
;; (let [stoper (->> (rx/filter #(= ::uev/interrupt %) stream)
;; (rx/take 1))]
;; (->> streams/mouse-position
;; (rx/take-until stoper)
;; (rx/map (juxt :viewport :ctrl))
;; (rx/map (fn [[pt ctrl?]]
;; (update-ruler pt ctrl?)))))))
;; (defn start-ruler
;; []
;; (StartRuler.))
;; ;; --- Update Ruler
;; (deftype UpdateRuler [point ctrl?]
;; ptk/UpdateEvent
;; (update [_ state]
;; (let [pid (get-in state [:workspace :current])
;; ruler (get-in state [:workspace pid :ruler])]
;; (if-not ctrl?
;; (assoc-in state [:workspace pid :ruler :end] point)
;; (let [start (get-in state [:workspace pid :ruler :start])
;; end (-> (gpt/subtract point start)
;; (align-position)
;; (gpt/add start))]
;; (assoc-in state [:workspace pid :ruler :end] end))))))
;; (defn update-ruler
;; [point ctrl?]
;; {:pre [(gpt/point? point)
;; (boolean? ctrl?)]}
;; (UpdateRuler. point ctrl?))
;; ;; --- Clear Ruler
;; (deftype ClearRuler []
;; ptk/UpdateEvent
;; (update [_ state]
;; (let [pid (get-in state [:workspace :current])]
;; (update-in state [:workspace pid] dissoc :ruler))))
;; (defn clear-ruler
;; []
;; (ClearRuler.))

View file

@ -21,9 +21,9 @@
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as ms]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as gsh]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as gsh]
[uxbox.main.snap :as snap]))
;; -- Declarations

View file

@ -13,10 +13,10 @@
[rumext.alpha :as mf]
[uxbox.common.uuid :as uuid]
[uxbox.common.pages :as cp]
[uxbox.util.math :as mth]
[uxbox.util.geom.shapes :as geom]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]
[uxbox.common.math :as mth]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.matrix :as gmt]
[uxbox.main.ui.shapes.frame :as frame]
[uxbox.main.ui.shapes.circle :as circle]
[uxbox.main.ui.shapes.icon :as icon]

View file

@ -11,8 +11,8 @@
(:require
[beicon.core :as rx]
[uxbox.common.uuid :refer [zero]]
[uxbox.util.math :as mth]
[uxbox.util.geom.point :as gpt]
[uxbox.common.math :as mth]
[uxbox.common.geom.point :as gpt]
[uxbox.main.worker :as uw]
[uxbox.util.geom.snap-points :as sp]))

View file

@ -10,7 +10,7 @@
[beicon.core :as rx]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.geom.point :as gpt]))
;; --- User Events

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]))
(mf/defc circle-shape

View file

@ -7,7 +7,7 @@
(ns uxbox.main.ui.shapes.custom-stroke
(:require
[rumext.alpha :as mf]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]))
; The SVG standard does not implement yet the 'stroke-alignment'

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.common.data :as d]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]))
(def frame-default-props {:fill-color "#ffffff"})

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.debug :refer [debug?]]
[uxbox.util.geom.shapes :as geom]))
[uxbox.common.geom.shapes :as geom]))
(defn group-shape
[shape-wrapper]

View file

@ -10,7 +10,7 @@
(ns uxbox.main.ui.shapes.icon
(:require
[rumext.alpha :as mf]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.object :as obj]))

View file

@ -10,7 +10,7 @@
(ns uxbox.main.ui.shapes.image
(:require
[rumext.alpha :as mf]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.object :as obj]))

View file

@ -13,7 +13,7 @@
[rumext.alpha :as mf]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]))
;; --- Path Shape

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]))
(mf/defc rect-shape

View file

@ -8,10 +8,10 @@
(:require
[rumext.alpha :as mf]
[uxbox.common.data :as d]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.matrix :as gmt]
[uxbox.main.fonts :as fonts]
[uxbox.util.geom.shapes :as geom]
[uxbox.util.object :as obj]
[uxbox.util.geom.matrix :as gmt]))
[uxbox.util.object :as obj]))
;; --- Text Editor Rendering

View file

@ -20,7 +20,7 @@
[uxbox.util.dom :as dom]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.util.router :as rt]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.common.uuid :as uuid]
[uxbox.util.webapi :as wapi]))

View file

@ -24,9 +24,9 @@
[uxbox.main.ui.shapes.rect :as rect]
[uxbox.main.ui.shapes.text :as text]
[uxbox.util.object :as obj]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]))
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]))
(defn on-mouse-down
[event {:keys [interactions] :as shape}]

View file

@ -22,10 +22,10 @@
[uxbox.main.exports :as exports]
[uxbox.util.data :refer [classnames]]
[uxbox.util.dom :as dom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.util.router :as rt]
[uxbox.main.data.viewer :as vd])
(:import goog.events.EventType

View file

@ -34,7 +34,7 @@
[uxbox.main.ui.workspace.left-toolbar :refer [left-toolbar]]
[uxbox.util.data :refer [classnames]]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.geom.point :as gpt]))
;; --- Workspace

View file

@ -12,17 +12,17 @@
[rumext.alpha :as mf]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as dw]
[uxbox.util.geom.shapes :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as ms]
[uxbox.main.ui.workspace.shapes :as shapes]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.util.dom :as dom]
[uxbox.util.data :refer [seek]]
[uxbox.util.geom.matrix :as gmt]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.geom.path :as path]
[uxbox.util.geom.point :as gpt]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.main.snap :as snap]
[uxbox.common.uuid :as uuid]))

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.main.refs :as refs]
[uxbox.common.pages :as cp]
[uxbox.util.geom.shapes :as gsh]
[uxbox.common.geom.shapes :as gsh]
[uxbox.util.geom.grid :as gg]))
(mf/defc square-grid [{:keys [frame zoom grid] :as props}]

View file

@ -23,7 +23,7 @@
[uxbox.main.ui.workspace.presence :as presence]
[uxbox.util.i18n :as i18n :refer [t]]
[uxbox.util.data :refer [classnames]]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.util.router :as rt]))
;; --- Zoom Widget

View file

@ -10,7 +10,7 @@
(ns uxbox.main.ui.workspace.rules
(:require
[rumext.alpha :as mf]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.util.object :as obj]))
(defn- calculate-step-size

View file

@ -11,7 +11,7 @@
[potok.core :as ptk]
[uxbox.main.refs :as refs]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.geom.point :as gpt]))
;; FIXME: revisit this ns in order to find a better location for its functions
;; TODO: this need a good refactor (probably move to events with access to the state)

View file

@ -15,14 +15,14 @@
[rumext.alpha :as mf]
[rumext.util :refer [map->obj]]
[uxbox.main.data.workspace :as dw]
[uxbox.util.geom.shapes :as geom]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as ms]
[uxbox.util.dom :as dom]
[uxbox.util.object :as obj]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.matrix :as gmt]
[uxbox.util.debug :refer [debug?]]))
(defn rotation-cursor [angle]
@ -161,7 +161,7 @@
(mf/defc resize-side-handler [{:keys [x y length angle zoom position transform on-resize]}]
[:rect {:x (+ x (/ resize-point-rect-size zoom))
:y (- y (/ resize-side-height 2 zoom))
:width (- length (/ (* resize-point-rect-size 2) zoom))
:width (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
:height (/ resize-side-height zoom)
:transform (gmt/multiply transform
(gmt/rotate-matrix angle (gpt/point x y)))

View file

@ -26,7 +26,7 @@
[uxbox.main.ui.workspace.shapes.group :as group]
[uxbox.main.ui.workspace.shapes.path :as path]
[uxbox.main.ui.workspace.shapes.text :as text]
[uxbox.util.geom.shapes :as geom]))
[uxbox.common.geom.shapes :as geom]))
(declare group-wrapper)
(declare frame-wrapper)

View file

@ -9,9 +9,9 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]
[uxbox.util.debug :as debug]
[uxbox.util.geom.shapes :as geom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.debug :refer [debug?]]
["randomcolor" :as rdcolor]))

View file

@ -15,9 +15,9 @@
[uxbox.main.store :as st]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.util.dom :as dom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]))
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]))
(defn- on-mouse-down
[event {:keys [id type] :as shape}]

View file

@ -17,9 +17,9 @@
[uxbox.main.store :as st]
[uxbox.main.ui.workspace.shapes.common :as common]
[uxbox.main.ui.shapes.frame :as frame]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.dom :as dom]
[uxbox.main.streams :as ms]
[uxbox.util.timers :as ts]))

View file

@ -14,13 +14,12 @@
[cuerdas.core :as str]
[uxbox.util.data :as dt]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.shapes :as geom]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]
[uxbox.main.data.workspace :as dw]
[uxbox.main.ui.keyboard :as kbd]
))
[uxbox.main.ui.keyboard :as kbd]))
(defn- get-click-interaction
[shape]

View file

@ -19,9 +19,6 @@
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.ui.workspace.shapes.common :as common]
[uxbox.util.dom :as dom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.shapes :as geom]
[uxbox.util.interop :as itr]
[uxbox.main.streams :as ms]
[uxbox.util.timers :as ts]))

View file

@ -22,9 +22,8 @@
[uxbox.main.fonts :as fonts]
[uxbox.util.color :as color]
[uxbox.util.dom :as dom]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.object :as obj]
[uxbox.util.geom.matrix :as gmt]
["slate" :as slate]
["slate-react" :as rslate])
(:import

View file

@ -13,9 +13,9 @@
[rumext.alpha :as mf]
[uxbox.common.data :as d]
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.math :as math]
[uxbox.common.math :as math]
[uxbox.main.store :as st]
[uxbox.main.data.workspace :as udw]
[uxbox.main.ui.icons :as i]

View file

@ -12,7 +12,7 @@
[rumext.alpha :as mf]
[uxbox.util.dom :as dom]
[uxbox.util.data :as d]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.common.data :refer [parse-integer]]
[uxbox.main.store :as st]
[uxbox.main.refs :as refs]

View file

@ -15,10 +15,10 @@
[uxbox.main.refs :as refs]
[uxbox.common.data :as d]
[uxbox.util.dom :as dom]
[uxbox.util.geom.shapes :as gsh]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.shapes :as gsh]
[uxbox.common.geom.point :as gpt]
[uxbox.main.data.workspace :as udw]
[uxbox.util.math :as math]
[uxbox.common.math :as math]
[uxbox.util.i18n :refer [t] :as i18n]))
;; -- User/drawing coords

View file

@ -10,7 +10,7 @@
(ns uxbox.main.ui.workspace.sidebar.options.rows.color-row
(:require
[rumext.alpha :as mf]
[uxbox.util.math :as math]
[uxbox.common.math :as math]
[uxbox.util.dom :as dom]
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]]

View file

@ -19,7 +19,7 @@
[uxbox.util.dom :as dom]
[uxbox.util.object :as obj]
[uxbox.util.i18n :as i18n :refer [tr t]]
[uxbox.util.math :as math]
[uxbox.common.math :as math]
[uxbox.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]))
(defn- stroke-menu-memo-equals?

View file

@ -5,7 +5,7 @@
[uxbox.main.refs :as refs]
[uxbox.main.snap :as snap]
[uxbox.util.geom.snap-points :as sp]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.geom.point :as gpt]))
(def ^:private line-color "#D383DA")

View file

@ -30,10 +30,10 @@
[uxbox.main.ui.workspace.presence :as presence]
[uxbox.main.ui.workspace.snap-feedback :refer [snap-feedback]]
[uxbox.main.ui.workspace.frame-grid :refer [frame-grid]]
[uxbox.util.math :as mth]
[uxbox.common.math :as mth]
[uxbox.util.dom :as dom]
[uxbox.util.object :as obj]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.perf :as perf]
[uxbox.common.uuid :as uuid])
(:import goog.events.EventType))

View file

@ -11,7 +11,7 @@
"Color conversion utils."
(:require
[cuerdas.core :as str]
[uxbox.util.math :as math]
[uxbox.common.math :as math]
[goog.color :as gcolor]))
(defn rgb->str

View file

@ -111,9 +111,6 @@
not-found))
not-found coll)))
(defn zip [col1 col2]
(map vector col1 col2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Numbers Parsing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -13,7 +13,7 @@
[cuerdas.core :as str]
[beicon.core :as rx]
[cuerdas.core :as str]
[uxbox.util.geom.point :as gpt]
[uxbox.common.geom.point :as gpt]
[uxbox.util.blob :as blob]
[uxbox.util.transit :as ts]))

View file

@ -1,229 +0,0 @@
/*
* 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 Andrey Antukh <niwi@niwi.nz>
*/
// NOTE: this code is unused, but is preserved for the case when we
// note that the cljs impl has not not enough performance.
goog.provide("uxbox.util.geom.matrix_impl");
goog.provide("uxbox.util.geom.matrix_impl.Matrix");
goog.require("goog.math");
goog.require("uxbox.util.geom.point_impl");
goog.scope(function() {
const self = uxbox.util.geom.matrix_impl;
const gpt = uxbox.util.geom.point_impl;
const math = goog.math;
/**
* @param {number} a
* @param {number} b
* @param {number} c
* @param {number} d
* @param {number} e
* @param {number} f
* @struct
*/
class Matrix {
constructor(a, b, c, d, e, f) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
}
[Symbol.iterator]() {
return [["a", this.a]
["b", this.b]];
}
toString() {
return `matrix(${this.a},${this.b},${this.c},${this.d},${this.e},${this.f})`;
}
copy() {
return new Matrix(
this.a,
this.b,
this.c,
this.d,
this.e,
this.f
);
}
}
self.Matrix = Matrix;
/**
* @param {number?} a
* @param {number?} b
* @param {number?} c
* @param {number?} d
* @param {number?} e
* @param {number?} f
* @return {Matrix}
*/
self.matrix = function(a, b, c, d, e, f) {
if (a === undefined) {
return new Matrix(1,0,0,1,0,0);
} else {
return new Matrix(a,b,c,d,e,f);
}
};
/**
* @param {?} m
* @return {boolean}
*/
function isMatrix(m) {
return m instanceof Matrix;
}
self.isMatrix = isMatrix
/**
* @param {Matrix} m1
* @param {Matrix} m2
* @return {Matrix}
*/
self.multiplyUnsafe = function(m1, m2) {
const a = m1.a * m2.a + m1.c * m2.b;
const b = m1.b * m2.a + m1.d * m2.b;
const c = m1.a * m2.c + m1.c * m2.d;
const d = m1.b * m2.c + m1.d * m2.d;
const e = m1.a * m2.e + m1.c * m2.f + m1.e;
const f = m1.b * m2.e + m1.d * m2.f + m1.f;
m1.a = a;
m1.b = b;
m1.c = c;
m1.d = d;
m1.e = e;
m1.f = f;
return m1;
}
/**
* @param {Matrix} m1
* @param {Matrix} m2
* @return {Matrix}
*/
self.multiply = function(m1, m2) {
m1 = m1.copy();
return self.multiplyUnsafe(m1, m2);
}
/**
* @param {...Matrix} matrices
* @return {Matrix}
*/
self.compose = function(...matrices) {
switch (matrices.length) {
case 0:
throw new Error('no matrices provided')
case 1:
return matrices[0]
case 2:
return self.multiply(matrices[0], matrices[1])
default: {
let result = matrices[0].copy();
for (let i=1; i<matrices.length; i++) {
result = self.multiplyUnsafe(result, matrices[i]);
}
return result;
}
}
};
/**
* @param {gpt.Point} p
* @return {Matrix}
*/
self.translateMatrix = function(p) {
return new Matrix(1, 0, 0, 1, p.x, p.y);
};
/**
* @param {gpt.Point} p
* @return {Matrix}
*/
self.scaleMatrix = function(p) {
return new Matrix(p.x, 0, 0, p.y, 0, 0);
};
/**
* @param {number} angle
* @return {Matrix}
*/
self.rotateMatrix = function(angle) {
const r = math.toRadiants(angle);
return new Matrix(
Math.cos(r),
Math.sin(r),
-Math.sin(r),
Math.cos(r),
0,
0
);
};
/**
* @param {Matrix} m
* @param {gpt.Point} p
* @return {Matrix}
*/
self.translate = function(m, p) {
return self.multiply(m, self.translateMatrix(p));
}
/**
* @param {Matrix} m
* @param {angle} angle
* @param {gpt.Point?} center
* @return {Matrix}
*/
self.rotate = function(m, angle, center) {
if (center === undefined) {
return self.multiply(m, self.rotateMatrix(angle));
} else {
return self.compose(
m,
self.translateMatrix(center),
self.rotateMatrix(angle),
self.translateMatrix(gpt.negate(center))
);
}
};
/**
* @param {Matrix} m
* @param {gpt.Point} scale
* @param {gpt.Point?} center
* @return {Matrix}
*/
self.scale = function(m, scale, center) {
if (center === undefined) {
return self.multiply(m, self.scaleMatrix(scale));
} else {
return self.compose(
m,
self.translateMatrix(center),
self.scaleMatrix(scale),
self.translateMatrix(gpt.negate(center))
);
}
};
});

View file

@ -1,242 +0,0 @@
/*
* 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 Andrey Antukh <niwi@niwi.nz>
*/
// NOTE: this code is unused, but is preserved for the case when we
// note that the cljs impl has not not enough performance.
goog.provide("uxbox.util.geom.point_impl");
goog.provide("uxbox.util.geom.point_impl.Point");
goog.require("goog.math");
goog.scope(function() {
const self = uxbox.util.geom.point_impl;
const math = goog.math;
/**
* @param {number} x
* @param {number} y
* @struct
*/
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return "point(" + this.x + ", " + this.y + ")";
}
}
self.Point = Point;
self.point = function(x, y) {
let xv = null;
let yv = null;
if (x === undefined) {
return new Point(0, 0);
} else {
xv = x;
}
if (y === undefined) {
yv = x;
} else {
yv = y;
}
return new Point(xv, yv);
};
function isPoint(p) {
return p instanceof Point;
}
self.isPoint = isPoint;
/**
* @param {Point} p
* @param {number} angle
* @return {Point}
*/
self.rotate = function(p, angle) {
const r = math.toRadians(angle);
const sin = Math.sin(r);
const cos = Math.cos(r);
const x = p.x;
const y = p.y;
const point = new Point(
x * cos - y * sin,
x * sin + y * cos
);
return self.roundTo(point, 6)
};
/**
* @param {Point} p
* @param {Point} other
* @return {Point}
*/
self.add = function(p, other) {
return new Point(
p.x + other.x,
p.y + other.y
);
};
/**
* @param {Point} p
* @param {Point} other
* @return {Point}
*/
self.subtract = function(p, other) {
return new Point(
p.x - other.x,
p.y - other.y
);
};
/**
* @param {Point} p
* @param {Point} other
* @return {Point}
*/
self.multiply = function(p, other) {
return new Point(
p.x * other.x,
p.y * other.y
);
};
/**
* @param {Point} p
* @param {Point} other
* @return {Point}
*/
self.divide = function(p, other) {
return new Point(
p.x / other.x,
p.y / other.y
);
};
/**
* @param {Point} p
* @return {Point}
*/
self.negate = function(p) {
const x = p.x, y = p.y;
return new Point(
x === 0 ? x : x * -1,
y === 0 ? y : y * -1
);
};
/**
* @param {Point} p
* @param {Point} other
* @return {number}
*/
self.distance = function(p, other) {
const dx = p.x - other.x;
const dy = p.y - other.y;
return Math.sqrt(Math.pow(dx, 2),
Math.pow(dy, 2));
};
/**
* @param {Point} p
* @param {Point} center
* @return {number}
*/
self.angle = function(p, center) {
if (center !== undefined) {
p = self.subtract(p, center);
}
return math.toDegrees(Math.atan2(p.y, p.x));
};
/**
* @param {Point} p
* @param {Point} other
* @return {number}
*/
self.length = function(p) {
return Math.sqrt(Math.pow(p.x, 2) + Math.pow(p.y, 2));
};
/**
* @param {Point} p
* @return {number}
*/
self.angle2other = function(p, other) {
let angle = ((p.x * other.x) + (p.y * other.y)) / (self.length(p) * self.length(other));
if (angle < -1) {
angle = -1;
} else if (angle > 1) {
angle = 1;
}
angle = Math.acos(angle);
angle = math.toDegrees(angle);
return parseFloat(angle.toFixed(6));
};
/**
* @param {Point} p
* @param {number} angle
* @return {Point}
*/
self.updateAngle = function(p, angle) {
const len = self.length(p);
const r = math.toRadiants(angle);
return new Point(
Math.cos(r) * len,
Math.sin(r) * len
);
};
/**
* @param {Point} p
* @param {number} decimals
* @return {Point}
*/
self.roundTo = function(p, decimals) {
return new Point(
parseFloat(p.x.toFixed(decimals)),
parseFloat(p.y.toFixed(decimals))
);
};
// class Matrix {
// constructor() {
// this.a = 1;
// this.b = 0;
// this.c = 0;
// this.d = 1;
// this.e = 0;
// this.f = 0;
// }
// }
// self = uxbox.util.geom.matrix_impl;
// self.Matrix = Matrix;
// self.sayHello = function() {
// console.log("hello");
// }
});

View file

@ -9,8 +9,8 @@
(ns uxbox.util.geom.grid
(:require
[uxbox.util.math :as mth]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.math :as mth]
[uxbox.common.geom.point :as gpt]))
(def ^:private default-items 12)

View file

@ -1,134 +0,0 @@
;; 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) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.matrix
(:require [cuerdas.core :as str]
[cognitect.transit :as t]
[uxbox.util.math :as mth]
[uxbox.util.geom.point :as gpt]))
;; --- Matrix Impl
(defrecord Matrix [a b c d e f]
Object
(toString [_]
(str "matrix(" a "," b "," c "," d "," e "," f ")")))
(defn multiply
([{m1a :a m1b :b m1c :c m1d :d m1e :e m1f :f :as m1}
{m2a :a m2b :b m2c :c m2d :d m2e :e m2f :f :as m2}]
(Matrix.
(+ (* m1a m2a) (* m1c m2b))
(+ (* m1b m2a) (* m1d m2b))
(+ (* m1a m2c) (* m1c m2d))
(+ (* m1b m2c) (* m1d m2d))
(+ (* m1a m2e) (* m1c m2f) m1e)
(+ (* m1b m2e) (* m1d m2f) m1f)))
([m1 m2 & others]
(reduce multiply (multiply m1 m2) others)))
(defn substract
[{m1a :a m1b :b m1c :c m1d :d m1e :e m1f :f :as m1}
{m2a :a m2b :b m2c :c m2d :d m2e :e m2f :f :as m2}]
(Matrix.
(- m1a m2a) (- m1b m2b) (- m1c m2c)
(- m1d m2d) (- m1e m2e) (- m1f m2f)))
(defn ^boolean matrix?
"Return true if `v` is Matrix instance."
[v]
(instance? Matrix v))
(defn matrix
"Create a new matrix instance."
([]
(Matrix. 1 0 0 1 0 0))
([a b c d e f]
(Matrix. a b c d e f)))
(def base (matrix))
(defn base?
[v]
(= v base))
(defn translate-matrix
[{x :x y :y :as pt}]
(assert (gpt/point? pt))
(Matrix. 1 0 0 1 x y))
(defn scale-matrix
([pt center]
(multiply (translate-matrix center)
(scale-matrix pt)
(translate-matrix (gpt/negate center))))
([{x :x y :y :as pt}]
(assert (gpt/point? pt))
(Matrix. x 0 0 y 0 0)))
(defn rotate-matrix
([angle point] (multiply (translate-matrix point)
(rotate-matrix angle)
(translate-matrix (gpt/negate point))))
([angle]
(let [a (mth/radians angle)]
(Matrix. (mth/cos a)
(mth/sin a)
(- (mth/sin a))
(mth/cos a)
0
0))))
(defn skew-matrix
([angle-x angle-y point]
(multiply (translate-matrix point)
(skew-matrix angle-y angle-y)
(translate-matrix (gpt/negate point))))
([angle-x angle-y]
(let [m1 (mth/tan (mth/radians angle-x))
m2 (mth/tan (mth/radians angle-y))]
(Matrix. 1 m2 m1 1 0 0))))
(defn rotate
"Apply rotation transformation to the matrix."
([m angle]
(multiply m (rotate-matrix angle)))
([m angle center]
(multiply m (rotate-matrix angle center))))
(defn scale
"Apply scale transformation to the matrix."
([m scale]
(multiply m (scale-matrix scale)))
([m scale center]
(multiply m (scale-matrix scale center))))
(defn translate
"Apply translate transformation to the matrix."
[m pt]
(multiply m (translate-matrix pt)))
(defn skew
"Apply translate transformation to the matrix."
([m angle-x angle-y]
(multiply m (skew-matrix angle-x angle-y)))
([m angle-x angle-y p]
(multiply m (skew-matrix angle-x angle-y p))))
;; --- Transit Adapter
(def matrix-write-handler
(t/write-handler
(constantly "matrix")
(fn [v] (into {} v))))
(def matrix-read-handler
(t/read-handler
(fn [value]
(map->Matrix value))))

View file

@ -1,198 +0,0 @@
;; 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) 2015-2020 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.geom.point
(:refer-clojure :exclude [divide min max])
(:require
[cljs.core :as c]
[cuerdas.core :as str]
[uxbox.util.math :as mth]
[cognitect.transit :as t]))
;; --- Point Impl
(defrecord Point [x y])
(defn s [{:keys [x y]}] (str "(" x "," y ")"))
(defn ^boolean point?
"Return true if `v` is Point instance."
[v]
(instance? Point v))
(defn point
"Create a Point instance."
([] (Point. 0 0))
([v]
(cond
(point? v)
v
(number? v)
(Point. v v)
:else
(throw (ex-info "Invalid arguments" {:v v}))))
([x y] (Point. x y)))
(defn add
"Returns the addition of the supplied value to both
coordinates of the point as a new point."
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (+ x ox) (+ y oy)))
(defn subtract
"Returns the subtraction of the supplied value to both
coordinates of the point as a new point."
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (- x ox) (- y oy)))
(defn multiply
"Returns the subtraction of the supplied value to both
coordinates of the point as a new point."
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (* x ox) (* y oy)))
(defn divide
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(Point. (/ x ox) (/ y oy)))
(defn min
[{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}]
(Point. (c/min x1 x2) (c/min y1 y2)))
(defn max
[{x1 :x y1 :y :as p1} {x2 :x y2 :y :as p2}]
(Point. (c/max x1 x2) (c/max y1 y2)))
(defn inverse
[{:keys [x y] :as p}]
(assert (point? p))
(Point. (/ 1 x) (/ 1 y)))
(defn negate
[{x :x y :y :as p}]
(assert (point? p))
(Point. (- x) (- y)))
(defn distance
"Calculate the distance between two points."
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(let [dx (- x ox)
dy (- y oy)]
(-> (mth/sqrt (+ (mth/pow dx 2)
(mth/pow dy 2)))
(mth/precision 6))))
(defn length
[{x :x y :y :as p}]
(assert (point? p))
(mth/sqrt (+ (mth/pow x 2)
(mth/pow y 2))))
(defn angle
"Returns the smaller angle between two vectors.
If the second vector is not provided, the angle
will be measured from x-axis."
([{x :x y :y :as p}]
(-> (mth/atan2 y x)
(mth/degrees)))
([p center]
(angle (subtract p center))))
(defn angle-with-other
"Consider point as vector and calculate
the angle between two vectors."
[{x :x y :y :as p} {ox :x oy :y :as other}]
(assert (point? p))
(assert (point? other))
(let [a (/ (+ (* x ox)
(* y oy))
(* (length p)
(length other)))
a (mth/acos (if (< a -1) -1 (if (> a 1) 1 a)))]
(-> (mth/degrees a)
(mth/precision 6))))
(defn update-angle
"Update the angle of the point."
[p angle]
(assert (point? p))
(assert (number? angle))
(let [len (length p)
angle (mth/radians angle)]
(Point. (* (mth/cos angle) len)
(* (mth/sin angle) len))))
(defn quadrant
"Return the quadrant of the angle of the point."
[{:keys [x y] :as p}]
(assert (point? p))
(if (>= x 0)
(if (>= y 0) 1 4)
(if (>= y 0) 2 3)))
(defn round
"Change the precision of the point coordinates."
([point] (round point 0))
([{:keys [x y] :as p} decimanls]
(assert (point? p))
(assert (number? decimanls))
(Point. (mth/precision x decimanls)
(mth/precision y decimanls))))
(defn transform
"Transform a point applying a matrix transfomation."
[{:keys [x y] :as p} {:keys [a b c d e f] :as m}]
(assert (point? p))
(Point. (+ (* x a) (* y c) e)
(+ (* x b) (* y d) f)))
;; --- Transit Adapter
(def point-write-handler
(t/write-handler
(constantly "point")
(fn [v] (into {} v))))
(def point-read-handler
(t/read-handler
(fn [value]
(map->Point value))))
;; Vector functions
(defn to-vec [p1 p2]
(subtract p2 p1))
(defn dot [{x1 :x y1 :y} {x2 :x y2 :y}]
(+ (* x1 x2) (* y1 y2)))
(defn unit [v]
(let [v-length (length v)]
(divide v (point v-length v-length))))
(defn project [v1 v2]
(let [v2-unit (unit v2)
scalar-projection (dot v1 (unit v2))]
(multiply
v2-unit
(point scalar-projection scalar-projection))))

View file

@ -1,25 +0,0 @@
;; 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.util.geom.range-tree
(:require
[cljs.spec.alpha :as s]))
(defn make-tree [objects])
(defn add-shape [shape])
(defn remove-shape [shape])
(defn update-shape [old-shape new-shape])
(defn query [point match-dist]) ;; Return {:x => [(point, distance, shape-id)]}
;;

View file

@ -1,767 +0,0 @@
;; 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.util.geom.shapes
(:require
[clojure.spec.alpha :as s]
[uxbox.common.pages :as cp]
[uxbox.common.spec :as us]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.math :as mth]
[uxbox.util.data :as d]
[uxbox.util.debug :as debug]))
;; --- Relative Movement
(declare move-rect)
(declare move-path)
(defn- _chk
"Function that checks if a number is nil or nan. Will return 0 when not
valid and the number otherwise."
[v]
(if (or (not v) (mth/nan? v)) 0 v))
(defn move
"Move the shape relativelly to its current
position applying the provided delta."
[shape dpoint]
(case (:type shape)
:curve (move-path shape dpoint)
:path (move-path shape dpoint)
(move-rect shape dpoint)))
(defn- move-rect
"A specialized function for relative movement
for rect-like shapes."
[shape {dx :x dy :y}]
(assoc shape
:x (+ (_chk (:x shape)) (_chk dx))
:y (+ (_chk (:y shape)) (_chk dy))))
(defn- move-path
"A specialized function for relative movement
for path shapes."
[shape {dx :x dy :y}]
(let [segments (:segments shape)
xf (comp
(map #(update % :x + dx))
(map #(update % :y + dy)))]
(assoc shape :segments (into [] xf segments))))
(defn recursive-move
"Move the shape and all its recursive children."
[shape dpoint objects]
(let [children-ids (cp/get-children (:id shape) objects)
children (map #(get objects %) children-ids)]
(map #(move % dpoint) (cons shape children))))
;; --- Absolute Movement
(declare absolute-move-rect)
(defn absolute-move
"Move the shape to the exactly specified position."
[shape position]
(case (:type shape)
:path shape
:curve shape
(absolute-move-rect shape position)))
(defn- absolute-move-rect
"A specialized function for absolute moviment
for rect-like shapes."
[shape {:keys [x y] :as pos}]
(let [dx (if x (- (_chk x) (_chk (:x shape))) 0)
dy (if y (- (_chk y) (_chk (:y shape))) 0)]
(move shape (gpt/point dx dy))))
;; --- Center
(declare center-rect)
(declare center-path)
(defn center
"Calculate the center of the shape."
[shape]
(case (:type shape)
:curve (center-path shape)
:path (center-path shape)
(center-rect shape)))
(defn- center-rect
[{:keys [x y width height] :as shape}]
(gpt/point (+ x (/ width 2)) (+ y (/ height 2))))
(defn- center-path
[{:keys [segments] :as shape}]
(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))]
(gpt/point (/ (+ minx maxx) 2) (/ (+ miny maxy) 2))))
(defn center->rect
"Creates a rect given a center and a width and height"
[center width height]
{:x (- (:x center) (/ width 2))
:y (- (:y center) (/ height 2))
:width width
:height height})
;; --- Proportions
(declare assign-proportions-path)
(declare assign-proportions-rect)
(defn assign-proportions
[{:keys [type] :as shape}]
(case type
:path (assign-proportions-path shape)
(assign-proportions-rect shape)))
(defn- assign-proportions-rect
[{:keys [width height] :as shape}]
(assoc shape :proportion (/ width height)))
;; --- Paths
(defn update-path-point
"Update a concrete point in the path.
The point should exists before, this function
does not adds it automatically."
[shape index point]
(assoc-in shape [:segments index] point))
;; --- Setup Proportions
(declare setup-proportions-const)
(declare setup-proportions-image)
(defn setup-proportions
[shape]
(case (:type shape)
:icon (setup-proportions-image shape)
:image (setup-proportions-image shape)
:text shape
(setup-proportions-const shape)))
(defn setup-proportions-image
[{:keys [metadata] :as shape}]
(let [{:keys [width height]} metadata]
(assoc shape
:proportion (/ width height)
:proportion-lock false)))
(defn setup-proportions-const
[shape]
(assoc shape
:proportion 1
:proportion-lock false))
;; --- Resize (Dimentsions)
(defn resize-rect
[shape attr value]
(us/assert map? shape)
(us/assert #{:width :height} attr)
(us/assert number? value)
(let [{:keys [proportion proportion-lock]} shape]
(if-not proportion-lock
(assoc shape attr value)
(if (= attr :width)
(-> shape
(assoc :width value)
(assoc :height (/ value proportion)))
(-> shape
(assoc :height value)
(assoc :width (* value proportion)))))))
;; --- Setup (Initialize)
(declare setup-rect)
(declare setup-image)
(defn setup
"A function that initializes the first coordinates for
the shape. Used mainly for draw operations."
[shape props]
(case (:type shape)
:image (setup-image shape props)
(setup-rect shape props)))
(defn- setup-rect
"A specialized function for setup rect-like shapes."
[shape {:keys [x y width height]}]
(assoc shape
:x x
:y y
:width width
:height height))
(defn- setup-image
[{:keys [metadata] :as shape} {:keys [x y width height] :as props}]
(assoc shape
:x x
:y y
:width width
:height height
:proportion (/ (:width metadata)
(:height metadata))
:proportion-lock true))
;; --- Coerce to Rect-like shape.
(declare path->rect-shape)
(declare group->rect-shape)
(declare rect->rect-shape)
(defn shape->rect-shape
"Coerce shape to rect like shape."
[{:keys [type] :as shape}]
(case type
:path (path->rect-shape shape)
:curve (path->rect-shape shape)
(rect->rect-shape shape)))
(defn shapes->rect-shape
[shapes]
(let [shapes (mapv shape->rect-shape shapes)
minx (transduce (map :x1) min shapes)
miny (transduce (map :y1) min shapes)
maxx (transduce (map :x2) max shapes)
maxy (transduce (map :y2) max shapes)]
{:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny)
:type :rect}))
(declare rect->path)
(defn shape->path
[shape]
(case (:type shape)
:path shape
:curve shape
(rect->path shape)))
(defn rect->path
[{:keys [x y width height] :as shape}]
(let [points [(gpt/point x y)
(gpt/point (+ x width) y)
(gpt/point (+ x width) (+ y height))
(gpt/point x (+ y height))
(gpt/point x y)]]
(-> shape
(assoc :type :path)
(assoc :segments points))))
;; --- SHAPE -> RECT
(defn- rect->rect-shape
[{:keys [x y width height] :as shape}]
(assoc shape
:x1 x
:y1 y
:x2 (+ x width)
:y2 (+ y height)))
(defn- path->rect-shape
[{:keys [segments] :as shape}]
(let [minx (transduce (map :x) min segments)
miny (transduce (map :y) min segments)
maxx (transduce (map :x) max segments)
maxy (transduce (map :y) max segments)]
(assoc shape
:type :rect
:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny))))
;; --- Resolve Shape
(declare resolve-rect-shape)
(declare translate-from-frame)
(declare translate-to-frame)
(defn resolve-shape
[objects shape]
(case (:type shape)
:rect (resolve-rect-shape objects shape)
:group (resolve-rect-shape objects shape)
:frame (resolve-rect-shape objects shape)))
(defn- resolve-rect-shape
[objects {:keys [parent] :as shape}]
(loop [pobj (get objects parent)]
(if (= :frame (:type pobj))
(translate-from-frame shape pobj)
(recur (get objects (:parent pobj))))))
;; --- Transform Shape
(declare transform-rect)
(declare transform-path)
(defn transform
"Apply the matrix transformation to shape."
[{:keys [type] :as shape} xfmt]
(if (gmt/matrix? xfmt)
(case type
:path (transform-path shape xfmt)
:curve (transform-path shape xfmt)
(transform-rect shape xfmt))
shape))
(defn center-transform [shape matrix]
(let [shape-center (center shape)]
(-> shape
(transform
(-> (gmt/matrix)
(gmt/translate shape-center)
(gmt/multiply matrix)
(gmt/translate (gpt/negate shape-center)))))))
(defn- transform-rect
[{:keys [x y width height] :as shape} mx]
(let [tl (gpt/transform (gpt/point x y) mx)
tr (gpt/transform (gpt/point (+ x width) y) mx)
bl (gpt/transform (gpt/point x (+ y height)) mx)
br (gpt/transform (gpt/point (+ x width) (+ y height)) mx)
;; TODO: replace apply with transduce (performance)
minx (apply min (map :x [tl tr bl br]))
maxx (apply max (map :x [tl tr bl br]))
miny (apply min (map :y [tl tr bl br]))
maxy (apply max (map :y [tl tr bl br]))]
(assoc shape
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny))))
(defn- transform-path
[{:keys [segments] :as shape} xfmt]
(let [segments (mapv #(gpt/transform % xfmt) segments)]
(assoc shape :segments segments)))
;; --- Outer Rect
(declare transform-apply-modifiers)
(defn selection-rect-shape
[shape]
(-> shape
(transform-apply-modifiers)
(shape->rect-shape)))
(defn selection-rect
"Returns a rect that contains all the shapes and is aware of the
rotation of each shape. Mainly used for multiple selection."
[shapes]
(let [xf-resolve-shape (map selection-rect-shape)
shapes (into [] xf-resolve-shape shapes)
minx (transduce (map :x1) min shapes)
miny (transduce (map :y1) min shapes)
maxx (transduce (map :x2) max shapes)
maxy (transduce (map :y2) max shapes)]
{:x1 minx
:y1 miny
:x2 maxx
:y2 maxy
:x minx
:y miny
:width (- maxx minx)
:height (- maxy miny)
:type :rect}))
(defn translate-to-frame
[shape {:keys [x y] :as frame}]
(move shape (gpt/point (- x) (- y))))
(defn translate-from-frame
[shape {:keys [x y] :as frame}]
(move shape (gpt/point x y)))
;; --- Alignment
(s/def ::align-axis #{:hleft :hcenter :hright :vtop :vcenter :vbottom})
(declare calc-align-pos)
(defn align-to-rect
"Move the shape so that it is aligned with the given rectangle
in the given axis. Take account the form of the shape and the
possible rotation. What is aligned is the rectangle that wraps
the shape with the given rectangle. If the shape is a group,
move also all of its recursive children."
[shape rect axis objects]
(let [wrapper-rect (selection-rect [shape])
align-pos (calc-align-pos wrapper-rect rect axis)
delta {:x (- (:x align-pos) (:x wrapper-rect))
:y (- (:y align-pos) (:y wrapper-rect))}]
(recursive-move shape delta objects)))
(defn calc-align-pos
[wrapper-rect rect axis]
(case axis
:hleft (let [left (:x rect)]
{:x left
:y (:y wrapper-rect)})
:hcenter (let [center (+ (:x rect) (/ (:width rect) 2))]
{:x (- center (/ (:width wrapper-rect) 2))
:y (:y wrapper-rect)})
:hright (let [right (+ (:x rect) (:width rect))]
{:x (- right (:width wrapper-rect))
:y (:y wrapper-rect)})
:vtop (let [top (:y rect)]
{:x (:x wrapper-rect)
:y top})
:vcenter (let [center (+ (:y rect) (/ (:height rect) 2))]
{:x (:x wrapper-rect)
:y (- center (/ (:height wrapper-rect) 2))})
:vbottom (let [bottom (+ (:y rect) (:height rect))]
{:x (:x wrapper-rect)
:y (- bottom (:height wrapper-rect))})))
;; --- Distribute
(s/def ::dist-axis #{:horizontal :vertical})
(defn distribute-space
"Distribute equally the space between shapes in the given axis. If
there is no space enough, it does nothing. It takes into account
the form of the shape and the rotation, what is distributed is
the wrapping recangles of the shapes. If any shape is a group,
move also all of its recursive children."
[shapes axis objects]
(let [coord (if (= axis :horizontal) :x :y)
other-coord (if (= axis :horizontal) :y :x)
size (if (= axis :horizontal) :width :height)
; The rectangle that wraps the whole selection
wrapper-rect (selection-rect shapes)
; Sort shapes by the center point in the given axis
sorted-shapes (sort-by #(coord (center %)) shapes)
; Each shape wrapped in its own rectangle
wrapped-shapes (map #(selection-rect [%]) sorted-shapes)
; The total space between shapes
space (reduce - (size wrapper-rect) (map size wrapped-shapes))]
(if (<= space 0)
shapes
(let [unit-space (/ space (- (count wrapped-shapes) 1))
; Calculate the distance we need to move each shape.
; The new position of each one is the position of the
; previous one plus its size plus the unit space.
deltas (loop [shapes' wrapped-shapes
start-pos (coord wrapper-rect)
deltas []]
(let [first-shape (first shapes')
delta (- start-pos (coord first-shape))
new-pos (+ start-pos (size first-shape) unit-space)]
(if (= (count shapes') 1)
(conj deltas delta)
(recur (rest shapes')
new-pos
(conj deltas delta)))))]
(mapcat #(recursive-move %1 {coord %2 other-coord 0} objects)
sorted-shapes deltas)))))
;; --- Helpers
(defn contained-in?
"Check if a shape is contained in the
provided selection rect."
[shape selrect]
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} (shape->rect-shape selrect)
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} (shape->rect-shape shape)]
(and (neg? (- sy1 ry1))
(neg? (- sx1 rx1))
(pos? (- sy2 ry2))
(pos? (- sx2 rx2)))))
(defn overlaps?
"Check if a shape overlaps with provided selection rect."
[shape selrect]
(let [{sx1 :x1 sx2 :x2 sy1 :y1 sy2 :y2} (shape->rect-shape selrect)
{rx1 :x1 rx2 :x2 ry1 :y1 ry2 :y2} (shape->rect-shape shape)]
(and (< rx1 sx2)
(> rx2 sx1)
(< ry1 sy2)
(> ry2 sy1))))
(defn has-point?
[shape position]
(let [{:keys [x y]} position
selrect {:x1 (- x 5)
:y1 (- y 5)
:x2 (+ x 5)
:y2 (+ y 5)
:x (- x 5)
:y (- y 5)
:width 10
:height 10
:type :rect}]
(overlaps? shape selrect)))
(defn calculate-rec-path-skew-angle
[path-shape]
(let [p1 (get-in path-shape [:segments 2])
p2 (get-in path-shape [:segments 3])
p3 (get-in path-shape [:segments 4])
v1 (gpt/to-vec p1 p2)
v2 (gpt/to-vec p2 p3)]
(- 90 (gpt/angle-with-other v1 v2))))
(defn calculate-rec-path-height
"Calculates the height of a paralelogram given by the path"
[path-shape]
(let [p1 (get-in path-shape [:segments 2])
p2 (get-in path-shape [:segments 3])
p3 (get-in path-shape [:segments 4])
v1 (gpt/to-vec p1 p2)
v2 (gpt/to-vec p2 p3)
angle (gpt/angle-with-other v1 v2)]
(* (gpt/length v2) (mth/sin (mth/radians angle)))))
(defn calculate-rec-path-rotation
[path-shape1 path-shape2 resize-vector]
(let [idx-1 0
idx-2 (cond (and (neg? (:x resize-vector)) (pos? (:y resize-vector))) 1
(and (neg? (:x resize-vector)) (neg? (:y resize-vector))) 2
(and (pos? (:x resize-vector)) (neg? (:y resize-vector))) 3
:else 0)
p1 (get-in path-shape1 [:segments idx-1])
p2 (get-in path-shape2 [:segments idx-2])
v1 (gpt/to-vec (center path-shape1) p1)
v2 (gpt/to-vec (center path-shape2) p2)
rot-angle (gpt/angle-with-other v1 v2)
rot-sign (if (> (* (:y v1) (:x v2)) (* (:x v1) (:y v2))) -1 1)]
(* rot-sign rot-angle)))
(defn transform-shape-point
"Transform a point around the shape center"
[point shape transform]
(let [shape-center (center shape)]
(gpt/transform
point
(-> (gmt/multiply
(gmt/translate-matrix shape-center)
transform
(gmt/translate-matrix (gpt/negate shape-center)))))))
(defn transform-apply-modifiers
[shape]
(let [modifiers (:modifiers shape)
ds-modifier (:displacement modifiers (gmt/matrix))
resize (:resize-vector modifiers (gpt/point 1 1))
origin (:resize-origin modifiers (gpt/point 0 0))
resize-transform (:resize-transform modifiers (gmt/matrix))
resize-transform-inverse (:resize-transform-inverse modifiers (gmt/matrix))
rt-modif (:rotation modifiers 0)
shape (-> shape
(transform ds-modifier))
shape-center (center shape)]
(-> (shape->path shape)
(transform (-> (gmt/matrix)
;; Applies the current resize transformation
(gmt/translate origin)
(gmt/multiply resize-transform)
(gmt/scale resize)
(gmt/multiply resize-transform-inverse)
(gmt/translate (gpt/negate origin))
;; Applies the stacked transformations
(gmt/translate shape-center)
(gmt/multiply (gmt/rotate-matrix rt-modif))
(gmt/multiply (:transform shape (gmt/matrix)))
(gmt/translate (gpt/negate shape-center)))))))
(defn rect-path-dimensions [rect-path]
(let [seg (:segments rect-path)
[width height] (mapv (fn [[c1 c2]] (gpt/distance c1 c2)) (take 2 (d/zip seg (rest seg))))]
{:width width
:height height}))
(defn calculate-stretch [shape-path transform-inverse]
(let [shape-center (center shape-path)
shape-path-temp (transform
shape-path
(-> (gmt/matrix)
(gmt/translate shape-center)
(gmt/multiply transform-inverse)
(gmt/translate (gpt/negate shape-center))))
shape-path-temp-rec (shape->rect-shape shape-path-temp)
shape-path-temp-dim (rect-path-dimensions shape-path-temp)]
(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]
(letfn [(check [num] (if (or (nil? num) (mth/nan? num)) 0 num))
(to-positive [num] (if (< num 1) 1 num))]
(-> rect-shape
(update :x check)
(update :y check)
(update :width (comp to-positive check))
(update :height (comp to-positive check)))))
(defn transform-rect-shape
[shape]
(let [;; Apply modifiers to the rect as a path so we have the end shape expected
shape-path (transform-apply-modifiers shape)
shape-center (center shape-path)
resize-vector (get-in shape [:modifiers :resize-vector] (gpt/point 1 1))
;; Reverse the current transformation stack to get the base rectangle
shape-path-temp (center-transform shape-path (:transform-inverse shape (gmt/matrix)))
shape-path-temp-dim (rect-path-dimensions shape-path-temp)
shape-path-temp-rec (shape->rect-shape shape-path-temp)
;; This rectangle is the new data for the current rectangle. We want to change our rectangle
;; to have this width, height, x, y
rec (center->rect shape-center (:width shape-path-temp-dim) (:height shape-path-temp-dim))
rec-path (rect->path rec)
;; The next matrix is a series of transformations we have to do to the previous rec so that
;; after applying them the end result is the `shape-path-temp`
;; This is compose of three transformations: skew, resize and rotation
stretch-matrix (gmt/matrix)
skew-angle (calculate-rec-path-skew-angle shape-path-temp)
;; When one of the axis is flipped we have to reverse the skew
skew-angle (if (neg? (* (:x resize-vector) (:y resize-vector))) (- skew-angle) skew-angle )
stretch-matrix (gmt/multiply stretch-matrix (gmt/skew-matrix skew-angle 0))
h1 (calculate-rec-path-height shape-path-temp)
h2 (calculate-rec-path-height (center-transform rec-path stretch-matrix))
stretch-matrix (gmt/multiply stretch-matrix (gmt/scale-matrix (gpt/point 1 (/ h1 h2))))
rotation-angle (calculate-rec-path-rotation (center-transform rec-path stretch-matrix) shape-path-temp resize-vector)
stretch-matrix (gmt/multiply (gmt/rotate-matrix rotation-angle) stretch-matrix)
;; This is the inverse to be able to remove the transformation
stretch-matrix-inverse (-> (gmt/matrix)
(gmt/scale (gpt/point 1 (/ h2 h1)))
(gmt/skew (- skew-angle) 0)
(gmt/rotate (- rotation-angle)))
new-shape (-> shape
(merge rec)
(update :x #(mth/precision % 2))
(update :y #(mth/precision % 2))
(fix-invalid-rect-values)
(update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix))
(update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]
new-shape))
(defn transform-path-shape
[shape]
(transform-apply-modifiers shape)
;; TODO: Addapt for paths is not working
#_(let [shape-path (transform-apply-modifiers shape)
shape-path-center (center shape-path)
shape-transform-inverse' (-> (gmt/matrix)
(gmt/translate shape-path-center)
(gmt/multiply (:transform-inverse shape (gmt/matrix)))
(gmt/multiply (gmt/rotate-matrix (- (:rotation-modifier shape 0))))
(gmt/translate (gpt/negate shape-path-center)))]
(-> shape-path
(transform shape-transform-inverse')
(add-rotate-transform (:rotation-modifier shape 0)))))
(defn transform-shape
"Transform the shape properties given the modifiers"
([shape] (transform-shape nil shape))
([frame shape]
(let [new-shape (case (:type shape)
:path (transform-path-shape shape)
:curve (transform-path-shape shape)
(transform-rect-shape shape))]
(-> new-shape
(translate-to-frame frame)
(update :rotation #(mod (+ % (get-in shape [:modifiers :rotation] 0)) 360))
(dissoc :modifiers)))))
(defn transform-matrix
"Returns a transformation matrix without changing the shape properties.
The result should be used in a `transform` attribute in svg"
([{:keys [x y] :as shape}]
(let [shape-center (center shape)]
(-> (gmt/matrix)
(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

@ -11,8 +11,8 @@
(:require
[cljs.spec.alpha :as s]
[clojure.set :as set]
[uxbox.util.geom.shapes :as gsh]
[uxbox.util.geom.point :as gpt]))
[uxbox.common.geom.shapes :as gsh]
[uxbox.common.geom.point :as gpt]))
(defn- frame-snap-points [{:keys [x y width height] :as frame}]
(into #{(gpt/point x y)

View file

@ -1,111 +0,0 @@
;; 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.util.math
"A collection of math utils."
(:require [goog.math :as math]))
(defn ^boolean nan?
[v]
(js/isNaN v))
(defn ^boolean finite?
[v]
(js/isFinite v))
(defn abs
[^number v]
(js/Math.abs v))
(defn sin
"Returns the sine of a number"
[^number v]
(js/Math.sin v))
(defn cos
"Returns the cosine of a number."
[^number v]
(js/Math.cos v))
(defn acos
"Returns the arccosine of a number."
[^number v]
(js/Math.acos v))
(defn tan
"Returns the tangent of a number."
[^number v]
(js/Math.tan v))
(defn atan2
"Returns the arctangent of the quotient of its arguments."
[^number x ^number y]
(js/Math.atan2 x y))
(defn neg
"Negate the number"
[^number v]
(- v))
(defn sqrt
"Returns the square root of a number."
[v]
(js/Math.sqrt v))
(defn pow
"Returns the base to the exponent power."
[b e]
(js/Math.pow b e))
(defn floor
"Returns the largest integer less than or
equal to a given number."
[^number v]
(js/Math.floor v))
(defn round
"Returns the value of a number rounded to
the nearest integer."
[^number v]
(js/Math.round v))
(defn ceil
"Returns the smallest integer greater than
or equal to a given number."
[^number v]
(js/Math.ceil v))
(defn precision
[^number v ^number n]
(when (and (number? v) (number? n))
(js/parseFloat (.toFixed v n))))
(defn precision-or-0
[^number v ^number n]
(if (.-toFixed v)
(js/parseFloat (.toFixed v n))
0))
(defn radians
"Converts degrees to radians."
[^number degrees]
(math/toRadians degrees))
(defn degrees
"Converts radians to degrees."
[^number radiants]
(math/toDegrees radiants))
(defn distance
"Calculate the distance between two points."
[[x1 y1] [x2 y2]]
(let [dx (- x1 x2)
dy (- y1 y2)]
(-> (sqrt (+ (pow dx 2) (pow dy 2)))
(precision 2))))

View file

@ -7,11 +7,12 @@
(ns uxbox.util.perf
"Performance profiling for react components."
(:require-macros [uxbox.util.perf])
(:require [uxbox.util.math :as math]
[rumext.alpha :as mf]
[goog.functions :as f]
["react" :as react]
["tdigest" :as td]))
(:require
[uxbox.common.math :as math]
[rumext.alpha :as mf]
[goog.functions :as f]
["react" :as react]
["tdigest" :as td]))
;; For use it, just wrap the component you want to profile with
;; `perf/profiler` component and pass a label for debug purpose.

View file

@ -7,8 +7,8 @@
(ns uxbox.util.transit
"A lightweight abstraction for transit serialization."
(:require [cognitect.transit :as t]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]
[uxbox.common.geom.point :as gpt]
[uxbox.common.geom.matrix :as gmt]
[uxbox.util.time :as dt]))
(deftype Blob [content]
@ -29,18 +29,40 @@
(fn [value]
(->Blob (js/JSON.parse value)))))
;; --- Transit adapters
(def point-write-handler
(t/write-handler
(constantly "point")
(fn [v] (into {} v))))
(def point-read-handler
(t/read-handler
(fn [value]
(gpt/map->Point value))))
(def matrix-write-handler
(t/write-handler
(constantly "matrix")
(fn [v] (into {} v))))
(def matrix-read-handler
(t/read-handler
(fn [value]
(gmt/map->Matrix value))))
;; --- Transit Handlers
(def ^:privare +read-handlers+
{"u" uuid
"jsonblob" blob-read-handler
"matrix" gmt/matrix-read-handler
"point" gpt/point-read-handler})
"matrix" matrix-read-handler
"point" point-read-handler})
(def ^:privare +write-handlers+
{gmt/Matrix gmt/matrix-write-handler
{gmt/Matrix matrix-write-handler
Blob blob-write-handler
gpt/Point gpt/point-write-handler})
gpt/Point point-write-handler})
;; --- Public Api
@ -56,4 +78,3 @@
(t/write w data))
(catch :default e
(throw e))))

View file

@ -16,7 +16,7 @@
[uxbox.common.pages :as cp]
[uxbox.common.uuid :as uuid]
[uxbox.worker.impl :as impl]
[uxbox.util.geom.shapes :as geom]
[uxbox.common.geom.shapes :as geom]
[uxbox.util.quadtree :as qdt]))
(defonce state (l/atom {}))