diff --git a/frontend/src/app/main/data/workspace/booleans.cljs b/frontend/src/app/main/data/workspace/booleans.cljs index d4aa7455a..b2be46791 100644 --- a/frontend/src/app/main/data/workspace/booleans.cljs +++ b/frontend/src/app/main/data/workspace/booleans.cljs @@ -18,6 +18,24 @@ [cuerdas.core :as str] [potok.core :as ptk])) +(def ^:const style-properties + [:fill-color + :fill-opacity + :fill-color-gradient + :fill-color-ref-file + :fill-color-ref-id + :stroke-color + :stroke-color-ref-file + :stroke-color-ref-id + :stroke-opacity + :stroke-style + :stroke-width + :stroke-alignment + :stroke-cap-start + :stroke-cap-end + :shadow + :blur]) + (defn selected-shapes [state] (let [objects (wsh/lookup-page-objects state)] @@ -31,6 +49,7 @@ (defn create-bool-data [type name shapes] (let [head (first shapes) + head-data (select-keys head style-properties) selrect (gsh/selection-rect shapes)] (-> {:id (uuid/next) :type :bool @@ -40,6 +59,7 @@ :name name ::index (::index head) :shapes []} + (merge head-data) (gsh/setup selrect)))) (defn create-bool diff --git a/frontend/src/app/main/ui/shapes/bool.cljs b/frontend/src/app/main/ui/shapes/bool.cljs index be7337084..9d8117401 100644 --- a/frontend/src/app/main/ui/shapes/bool.cljs +++ b/frontend/src/app/main/ui/shapes/bool.cljs @@ -6,70 +6,38 @@ (ns app.main.ui.shapes.bool (:require - [app.common.data :as d] [app.common.geom.shapes :as gsh] - [app.common.math :as mth] + [app.main.ui.hooks :refer [use-equal-memo]] [app.util.object :as obj] [app.util.path.bool :as pb] - [app.util.path.geom :as upg] [app.util.path.shapes-to-path :as stp] - [clojure.set :as set] [rumext.alpha :as mf])) -(mf/defc path-points - [{:keys [points color]}] - - [:* - (for [[idx {:keys [x y]}] (d/enumerate points)] - [:circle {:key (str "circle-" idx) - :cx x - :cy y - :r 5 - :style {:fill color - ;;:fillOpacity 0.5 - }}])]) - (defn bool-shape [shape-wrapper] (mf/fnc bool-shape {::mf/wrap-props false} [props] (let [frame (obj/get props "frame") - childs (obj/get props "childs") - shape-1 (stp/convert-to-path (nth childs 0)) - shape-2 (stp/convert-to-path (nth childs 1)) + shape (obj/get props "shape") + childs (obj/get props "childs")] - content-1 (-> shape-1 gsh/transform-shape (gsh/translate-to-frame frame) :content) - content-2 (-> shape-2 gsh/transform-shape (gsh/translate-to-frame frame) :content) - + (when (> (count childs) 1) + (let [shape-1 (stp/convert-to-path (nth childs 0)) + shape-2 (stp/convert-to-path (nth childs 1)) - [content-1' content-2'] (pb/content-intersect-split content-1 content-2) - - points-1 (->> (upg/content->points content-1') - (map #(hash-map :x (mth/round (:x %)) - :y (mth/round (:y %)))) - (into #{})) - - points-2 (->> (upg/content->points content-2') - (map #(hash-map :x (mth/round (:x %)) - :y (mth/round (:y %)))) - (into #{})) + content-1 (use-equal-memo (-> shape-1 :content gsh/transform-shape)) + content-2 (use-equal-memo (-> shape-2 :content gsh/transform-shape)) - points-3 (set/intersection points-1 points-2)] + content + (mf/use-memo + (mf/deps content-1 content-2) + #(pb/content-bool (:bool-type shape) content-1 content-2))] - [:* - [:& shape-wrapper {:shape (-> shape-1 #_(assoc :content content-1')) - :frame frame}] - - [:& shape-wrapper {:shape (-> shape-2 #_(assoc :content content-2')) - :frame frame}] - - [:& path-points {:points points-1 :color "#FF0000"}] - [:& path-points {:points points-2 :color "#0000FF"}] - [:& path-points {:points points-3 :color "#FF00FF"}] - - - ]))) + [:& shape-wrapper {:shape (-> shape + (assoc :type :path) + (assoc :content content)) + :frame frame}]))))) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 327864592..8e2b43b78 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -32,14 +32,13 @@ (dom/stop-propagation event)) (mf/defc menu-entry - [{:keys [title shortcut submenu-ref on-click children] :as props}] - (let [entry-ref (mf/use-ref nil) - submenu-ref (mf/use-ref nil) + [{:keys [title shortcut on-click children] :as props}] + (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) on-pointer-enter (mf/use-callback - (fn [event] + (fn [] (mf/set-ref-val! hovering? true) (let [submenu-node (mf/ref-val submenu-ref)] (when (some? submenu-node) @@ -47,7 +46,7 @@ on-pointer-leave (mf/use-callback - (fn [event] + (fn [] (mf/set-ref-val! hovering? false) (let [submenu-node (mf/ref-val submenu-ref)] (when (some? submenu-node) @@ -227,7 +226,12 @@ :on-click do-boolean-intersection}] [:& menu-entry {:title (tr "workspace.shape.menu.exclude") :shortcut (sc/get-tooltip :boolean-exclude) - :on-click do-boolean-exclude}]] + :on-click do-boolean-exclude}] + + [:& menu-separator] + ;; TODO + [:& menu-entry {:title "Flatten"}] + [:& menu-entry {:title "Transform to path"}]] (if (:hidden shape) [:& menu-entry {:title (tr "workspace.shape.menu.show") diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index e99d25802..e0a62cbff 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -16,6 +16,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]] [app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]] [app.main.ui.workspace.sidebar.options.page :as page] + [app.main.ui.workspace.sidebar.options.shapes.bool :as bool] [app.main.ui.workspace.sidebar.options.shapes.circle :as circle] [app.main.ui.workspace.sidebar.options.shapes.frame :as frame] [app.main.ui.workspace.sidebar.options.shapes.group :as group] @@ -44,6 +45,7 @@ :path [:& path/options {:shape shape}] :image [:& image/options {:shape shape}] :svg-raw [:& svg-raw/options {:shape shape}] + :bool [:& bool/options {:shape shape}] nil) [:& exports-menu {:shape shape diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs new file mode 100644 index 000000000..dc5a8fa8c --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs @@ -0,0 +1,45 @@ +;; 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) UXBOX Labs SL + +(ns app.main.ui.workspace.sidebar.options.shapes.bool + (:require + [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] + [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] + [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] + [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] + [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]] + [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]] + [app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]] + [rumext.alpha :as mf])) + +(mf/defc options + [{:keys [shape] :as props}] + (let [ids [(:id shape)] + type (:type shape) + measure-values (select-keys shape measure-attrs) + stroke-values (select-keys shape stroke-attrs) + layer-values (select-keys shape layer-attrs) + constraint-values (select-keys shape constraint-attrs)] + [:* + [:& measures-menu {:ids ids + :type type + :values measure-values}] + [:& constraints-menu {:ids ids + :values constraint-values}] + [:& layer-menu {:ids ids + :type type + :values layer-values}] + [:& fill-menu {:ids ids + :type type + :values (select-keys shape fill-attrs)}] + [:& stroke-menu {:ids ids + :type type + :show-caps true + :values stroke-values}] + [:& shadow-menu {:ids ids + :values (select-keys shape [:shadow])}] + [:& blur-menu {:ids ids + :values (select-keys shape [:blur])}]])) diff --git a/frontend/src/app/util/path/bool.cljs b/frontend/src/app/util/path/bool.cljs index e6a41c884..d9c190286 100644 --- a/frontend/src/app/util/path/bool.cljs +++ b/frontend/src/app/util/path/bool.cljs @@ -14,7 +14,7 @@ [app.common.geom.shapes.rect :as gpr] [app.common.math :as mth] [app.util.path.geom :as upg] - [cuerdas.core :as str])) + [app.util.path.subpaths :as ups])) (def ^:const curve-curve-precision 0.1) @@ -267,3 +267,39 @@ (rest new-pending) new-content-b (conj new-content-a new-current)))))))) + + +(defn create-union [content-a content-b] + (d/concat + [] + content-a + (ups/reverse-content content-b))) + +(defn create-difference [content-a content-b] + (d/concat + [] + content-a + (ups/reverse-content content-b))) + +(defn create-intersection [content-a content-b] + (d/concat + [] + content-a + (ups/reverse-content content-b))) + + +(defn create-exclusion [content-a content-b] + (d/concat + [] + content-a + (ups/reverse-content content-b))) + +(defn content-bool + [bool-type content-a content-b] + + (let [[content-a' content-b'] (content-intersect-split content-a content-b)] + (case bool-type + :union (create-union content-a' content-b') + :difference (create-difference content-a' content-b') + :intersection (create-intersection content-a' content-b') + :exclusion (create-exclusion content-a' content-b')))) diff --git a/frontend/src/app/util/path/commands.cljs b/frontend/src/app/util/path/commands.cljs index 84a7725ef..fd1df9da9 100644 --- a/frontend/src/app/util/path/commands.cljs +++ b/frontend/src/app/util/path/commands.cljs @@ -199,3 +199,4 @@ (if (= prefix :c1) (command->point (get content (dec index))) (command->point (get content index)))) + diff --git a/frontend/src/app/util/path/subpaths.cljs b/frontend/src/app/util/path/subpaths.cljs index 010f1343a..5f0e2bf34 100644 --- a/frontend/src/app/util/path/subpaths.cljs +++ b/frontend/src/app/util/path/subpaths.cljs @@ -134,3 +134,14 @@ (->> closed-subpaths (mapcat :data) (into [])))) + +(defn reverse-content + "Given a content reverse the order of the commands" + [content] + + (->> content + (get-subpaths) + (mapv reverse-subpath) + (reverse) + (mapcat :data) + (into [])))