mirror of
https://github.com/penpot/penpot.git
synced 2026-09-10 14:09:17 +00:00
Merge pull request #9000 from penpot/niwinz-main-bugfixes
🐛 Add several fixes for app.common.types namespace
This commit is contained in:
commit
9cd1542dd9
@ -487,62 +487,3 @@
|
|||||||
b (+ (* bh 100) (* bv 10))]
|
b (+ (* bh 100) (* bv 10))]
|
||||||
(compare a b)))
|
(compare a b)))
|
||||||
|
|
||||||
(defn interpolate-color
|
|
||||||
[c1 c2 offset]
|
|
||||||
(cond
|
|
||||||
(<= offset (:offset c1)) (assoc c1 :offset offset)
|
|
||||||
(>= offset (:offset c2)) (assoc c2 :offset offset)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(let [tr-offset (/ (- offset (:offset c1)) (- (:offset c2) (:offset c1)))
|
|
||||||
[r1 g1 b1] (hex->rgb (:color c1))
|
|
||||||
[r2 g2 b2] (hex->rgb (:color c2))
|
|
||||||
a1 (:opacity c1)
|
|
||||||
a2 (:opacity c2)
|
|
||||||
r (+ r1 (* (- r2 r1) tr-offset))
|
|
||||||
g (+ g1 (* (- g2 g1) tr-offset))
|
|
||||||
b (+ b1 (* (- b2 b1) tr-offset))
|
|
||||||
a (+ a1 (* (- a2 a1) tr-offset))]
|
|
||||||
{:color (rgb->hex [r g b])
|
|
||||||
:opacity a
|
|
||||||
:r r
|
|
||||||
:g g
|
|
||||||
:b b
|
|
||||||
:alpha a
|
|
||||||
:offset offset})))
|
|
||||||
|
|
||||||
(defn- offset-spread
|
|
||||||
[from to num]
|
|
||||||
(->> (range 0 num)
|
|
||||||
(map #(mth/precision (+ from (* (/ (- to from) (dec num)) %)) 2))))
|
|
||||||
|
|
||||||
(defn uniform-spread?
|
|
||||||
"Checks if the gradient stops are spread uniformly"
|
|
||||||
[stops]
|
|
||||||
(let [cs (count stops)
|
|
||||||
from (first stops)
|
|
||||||
to (last stops)
|
|
||||||
expect-vals (offset-spread (:offset from) (:offset to) cs)
|
|
||||||
|
|
||||||
calculate-expected
|
|
||||||
(fn [expected-offset stop]
|
|
||||||
(and (mth/close? (:offset stop) expected-offset)
|
|
||||||
(let [ec (interpolate-color from to expected-offset)]
|
|
||||||
(and (= (:color ec) (:color stop))
|
|
||||||
(= (:opacity ec) (:opacity stop))))))]
|
|
||||||
(->> (map calculate-expected expect-vals stops)
|
|
||||||
(every? true?))))
|
|
||||||
|
|
||||||
(defn uniform-spread
|
|
||||||
"Assign an uniform spread to the offset values for the gradient"
|
|
||||||
[from to num-stops]
|
|
||||||
(->> (offset-spread (:offset from) (:offset to) num-stops)
|
|
||||||
(mapv (fn [offset]
|
|
||||||
(interpolate-color from to offset)))))
|
|
||||||
|
|
||||||
(defn interpolate-gradient
|
|
||||||
[stops offset]
|
|
||||||
(let [idx (d/index-of-pred stops #(<= offset (:offset %)))
|
|
||||||
start (if (= idx 0) (first stops) (get stops (dec idx)))
|
|
||||||
end (if (nil? idx) (last stops) (get stops idx))]
|
|
||||||
(interpolate-color start end offset)))
|
|
||||||
|
|||||||
@ -720,8 +720,10 @@
|
|||||||
|
|
||||||
(defn- offset-spread
|
(defn- offset-spread
|
||||||
[from to num]
|
[from to num]
|
||||||
(->> (range 0 num)
|
(if (<= num 1)
|
||||||
(map #(mth/precision (+ from (* (/ (- to from) (dec num)) %)) 2))))
|
[from]
|
||||||
|
(->> (range 0 num)
|
||||||
|
(map #(mth/precision (+ from (* (/ (- to from) (dec num)) %)) 2)))))
|
||||||
|
|
||||||
(defn uniform-spread?
|
(defn uniform-spread?
|
||||||
"Checks if the gradient stops are spread uniformly"
|
"Checks if the gradient stops are spread uniformly"
|
||||||
@ -750,6 +752,9 @@
|
|||||||
(defn interpolate-gradient
|
(defn interpolate-gradient
|
||||||
[stops offset]
|
[stops offset]
|
||||||
(let [idx (d/index-of-pred stops #(<= offset (:offset %)))
|
(let [idx (d/index-of-pred stops #(<= offset (:offset %)))
|
||||||
start (if (= idx 0) (first stops) (get stops (dec idx)))
|
start (cond
|
||||||
|
(nil? idx) (last stops)
|
||||||
|
(= idx 0) (first stops)
|
||||||
|
:else (get stops (dec idx)))
|
||||||
end (if (nil? idx) (last stops) (get stops idx))]
|
end (if (nil? idx) (last stops) (get stops idx))]
|
||||||
(interpolate-color start end offset)))
|
(interpolate-color start end offset)))
|
||||||
|
|||||||
@ -106,8 +106,9 @@
|
|||||||
(let [shape (get objects id)]
|
(let [shape (get objects id)]
|
||||||
(if (and (ctk/instance-head? shape) (seq children))
|
(if (and (ctk/instance-head? shape) (seq children))
|
||||||
children
|
children
|
||||||
(into (conj children shape)
|
(let [children' (conj children shape)]
|
||||||
(mapcat #(get-children-rec children %) (:shapes shape))))))]
|
(into children'
|
||||||
|
(mapcat #(get-children-rec children' %) (:shapes shape)))))))]
|
||||||
(get-children-rec [] id)))
|
(get-children-rec [] id)))
|
||||||
|
|
||||||
(defn get-component-shape
|
(defn get-component-shape
|
||||||
@ -440,7 +441,7 @@
|
|||||||
(if (ctk/main-instance? shape)
|
(if (ctk/main-instance? shape)
|
||||||
[shape]
|
[shape]
|
||||||
(if-let [children (cfh/get-children objects (:id shape))]
|
(if-let [children (cfh/get-children objects (:id shape))]
|
||||||
(mapcat collect-main-shapes children objects)
|
(mapcat #(collect-main-shapes % objects) children)
|
||||||
[])))
|
[])))
|
||||||
|
|
||||||
(defn get-component-from-shape
|
(defn get-component-from-shape
|
||||||
|
|||||||
@ -380,7 +380,7 @@
|
|||||||
nil))
|
nil))
|
||||||
|
|
||||||
(-nth [_ i default]
|
(-nth [_ i default]
|
||||||
(if (d/in-range? i size)
|
(if (d/in-range? size i)
|
||||||
(read-fill dbuffer mbuffer i)
|
(read-fill dbuffer mbuffer i)
|
||||||
default))
|
default))
|
||||||
|
|
||||||
|
|||||||
@ -278,7 +278,7 @@
|
|||||||
(set! (.-cache this) (c/-assoc cache k v))
|
(set! (.-cache this) (c/-assoc cache k v))
|
||||||
v)
|
v)
|
||||||
(do
|
(do
|
||||||
(set! (.-cache this) (assoc cache key nil))
|
(set! (.-cache this) (assoc cache k nil))
|
||||||
nil))))
|
nil))))
|
||||||
|
|
||||||
(-lookup [this k not-found]
|
(-lookup [this k not-found]
|
||||||
|
|||||||
@ -812,7 +812,7 @@
|
|||||||
:line-to
|
:line-to
|
||||||
(recur (cond-> points
|
(recur (cond-> points
|
||||||
(and from-p to-p)
|
(and from-p to-p)
|
||||||
(-> (conj! move-p)
|
(-> (conj! from-p)
|
||||||
(conj! to-p)))
|
(conj! to-p)))
|
||||||
(not-empty (subvec content 1))
|
(not-empty (subvec content 1))
|
||||||
to-p
|
to-p
|
||||||
|
|||||||
@ -262,7 +262,7 @@
|
|||||||
(or (nil? current) (= current-id parent-id))
|
(or (nil? current) (= current-id parent-id))
|
||||||
false
|
false
|
||||||
|
|
||||||
(cfh/frame-shape? current-id)
|
(cfh/frame-shape? current)
|
||||||
(:layout current)
|
(:layout current)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
@ -1439,7 +1439,7 @@
|
|||||||
(update-in [:layout-grid-cells id-from]
|
(update-in [:layout-grid-cells id-from]
|
||||||
assoc
|
assoc
|
||||||
:shapes (:shapes cell-to)
|
:shapes (:shapes cell-to)
|
||||||
:podition (:position cell-to))
|
:position (:position cell-to))
|
||||||
(update-in [:layout-grid-cells id-to]
|
(update-in [:layout-grid-cells id-to]
|
||||||
assoc
|
assoc
|
||||||
:shapes (:shapes cell-from)
|
:shapes (:shapes cell-from)
|
||||||
|
|||||||
@ -345,7 +345,6 @@
|
|||||||
(def typography-keys (set/union font-family-keys
|
(def typography-keys (set/union font-family-keys
|
||||||
font-size-keys
|
font-size-keys
|
||||||
font-weight-keys
|
font-weight-keys
|
||||||
font-weight-keys
|
|
||||||
letter-spacing-keys
|
letter-spacing-keys
|
||||||
line-height-keys
|
line-height-keys
|
||||||
text-case-keys
|
text-case-keys
|
||||||
|
|||||||
@ -1637,7 +1637,7 @@ Will return a value that matches this schema:
|
|||||||
[value]
|
[value]
|
||||||
(let [process-shadow (fn [shadow]
|
(let [process-shadow (fn [shadow]
|
||||||
(if (map? shadow)
|
(if (map? shadow)
|
||||||
(let [legacy-shadow-type (get "type" shadow)]
|
(let [legacy-shadow-type (get shadow "type")]
|
||||||
(-> shadow
|
(-> shadow
|
||||||
(set/rename-keys {"x" :offset-x
|
(set/rename-keys {"x" :offset-x
|
||||||
"offsetX" :offset-x
|
"offsetX" :offset-x
|
||||||
|
|||||||
@ -9,91 +9,8 @@
|
|||||||
#?(:cljs [goog.color :as gcolors])
|
#?(:cljs [goog.color :as gcolors])
|
||||||
[app.common.colors :as c]
|
[app.common.colors :as c]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.types.color :as colors]
|
|
||||||
[clojure.test :as t]))
|
[clojure.test :as t]))
|
||||||
|
|
||||||
(t/deftest valid-hex-color
|
|
||||||
(t/is (false? (colors/valid-hex-color? nil)))
|
|
||||||
(t/is (false? (colors/valid-hex-color? "")))
|
|
||||||
(t/is (false? (colors/valid-hex-color? "#")))
|
|
||||||
(t/is (false? (colors/valid-hex-color? "#qqqqqq")))
|
|
||||||
(t/is (true? (colors/valid-hex-color? "#aaa")))
|
|
||||||
(t/is (false? (colors/valid-hex-color? "#aaaa")))
|
|
||||||
(t/is (true? (colors/valid-hex-color? "#fabada"))))
|
|
||||||
|
|
||||||
(t/deftest valid-rgb-color
|
|
||||||
(t/is (false? (colors/valid-rgb-color? nil)))
|
|
||||||
(t/is (false? (colors/valid-rgb-color? "")))
|
|
||||||
(t/is (false? (colors/valid-rgb-color? "()")))
|
|
||||||
(t/is (true? (colors/valid-rgb-color? "(255, 30, 30)")))
|
|
||||||
(t/is (true? (colors/valid-rgb-color? "rgb(255, 30, 30)"))))
|
|
||||||
|
|
||||||
(t/deftest rgb-to-str
|
|
||||||
(t/is (= "rgb(1,2,3)" (colors/rgb->str [1 2 3])))
|
|
||||||
(t/is (= "rgba(1,2,3,4)" (colors/rgb->str [1 2 3 4]))))
|
|
||||||
|
|
||||||
(t/deftest rgb-to-hsv
|
|
||||||
;; (prn (colors/rgb->hsv [1 2 3]))
|
|
||||||
;; (prn (gcolors/rgbToHsv 1 2 3))
|
|
||||||
(t/is (= [210.0 0.6666666666666666 3.0] (colors/rgb->hsv [1.0 2.0 3.0])))
|
|
||||||
#?(:cljs (t/is (= (colors/rgb->hsv [1 2 3]) (vec (gcolors/rgbToHsv 1 2 3))))))
|
|
||||||
|
|
||||||
(t/deftest hsv-to-rgb
|
|
||||||
(t/is (= [1 2 3]
|
|
||||||
(colors/hsv->rgb [210 0.6666666666666666 3])))
|
|
||||||
#?(:cljs
|
|
||||||
(t/is (= (colors/hsv->rgb [210 0.6666666666666666 3])
|
|
||||||
(vec (gcolors/hsvToRgb 210 0.6666666666666666 3))))))
|
|
||||||
|
|
||||||
(t/deftest rgb-to-hex
|
|
||||||
(t/is (= "#010203" (colors/rgb->hex [1 2 3]))))
|
|
||||||
|
|
||||||
(t/deftest hex-to-rgb
|
|
||||||
(t/is (= [0 0 0] (colors/hex->rgb "#kkk")))
|
|
||||||
(t/is (= [1 2 3] (colors/hex->rgb "#010203"))))
|
|
||||||
|
|
||||||
(t/deftest format-hsla
|
|
||||||
(t/is (= "210, 50%, 0.78%, 1" (colors/format-hsla [210.0 0.5 0.00784313725490196 1])))
|
|
||||||
(t/is (= "220, 5%, 30%, 0.8" (colors/format-hsla [220.0 0.05 0.3 0.8]))))
|
|
||||||
|
|
||||||
(t/deftest format-rgba
|
|
||||||
(t/is (= "210, 199, 12, 0.08" (colors/format-rgba [210 199 12 0.08])))
|
|
||||||
(t/is (= "210, 199, 12, 1" (colors/format-rgba [210 199 12 1]))))
|
|
||||||
|
|
||||||
(t/deftest rgb-to-hsl
|
|
||||||
(t/is (= [210.0 0.5 0.00784313725490196] (colors/rgb->hsl [1 2 3])))
|
|
||||||
#?(:cljs (t/is (= (colors/rgb->hsl [1 2 3])
|
|
||||||
(vec (gcolors/rgbToHsl 1 2 3))))))
|
|
||||||
|
|
||||||
(t/deftest hsl-to-rgb
|
|
||||||
(t/is (= [1 2 3] (colors/hsl->rgb [210.0 0.5 0.00784313725490196])))
|
|
||||||
(t/is (= [210.0 0.5 0.00784313725490196] (colors/rgb->hsl [1 2 3])))
|
|
||||||
#?(:cljs (t/is (= (colors/hsl->rgb [210 0.5 0.00784313725490196])
|
|
||||||
(vec (gcolors/hslToRgb 210 0.5 0.00784313725490196))))))
|
|
||||||
|
|
||||||
(t/deftest expand-hex
|
|
||||||
(t/is (= "aaaaaa" (colors/expand-hex "a")))
|
|
||||||
(t/is (= "aaaaaa" (colors/expand-hex "aa")))
|
|
||||||
(t/is (= "aaaaaa" (colors/expand-hex "aaa")))
|
|
||||||
(t/is (= "aaaa" (colors/expand-hex "aaaa"))))
|
|
||||||
|
|
||||||
(t/deftest prepend-hash
|
|
||||||
(t/is "#aaa" (colors/prepend-hash "aaa"))
|
|
||||||
(t/is "#aaa" (colors/prepend-hash "#aaa")))
|
|
||||||
|
|
||||||
(t/deftest remove-hash
|
|
||||||
(t/is "aaa" (colors/remove-hash "aaa"))
|
|
||||||
(t/is "aaa" (colors/remove-hash "#aaa")))
|
|
||||||
|
|
||||||
(t/deftest color-string-pred
|
|
||||||
(t/is (true? (colors/color-string? "#aaa")))
|
|
||||||
(t/is (true? (colors/color-string? "(10,10,10)")))
|
|
||||||
(t/is (true? (colors/color-string? "rgb(10,10,10)")))
|
|
||||||
(t/is (true? (colors/color-string? "magenta")))
|
|
||||||
(t/is (false? (colors/color-string? nil)))
|
|
||||||
(t/is (false? (colors/color-string? "")))
|
|
||||||
(t/is (false? (colors/color-string? "kkkkkk"))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; app.common.colors tests
|
;; app.common.colors tests
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -387,55 +304,3 @@
|
|||||||
(t/is (= 0.25 (c/reduce-range 0.3 4)))
|
(t/is (= 0.25 (c/reduce-range 0.3 4)))
|
||||||
(t/is (= 0.0 (c/reduce-range 0.0 10))))
|
(t/is (= 0.0 (c/reduce-range 0.0 10))))
|
||||||
|
|
||||||
;; --- Gradient helpers
|
|
||||||
|
|
||||||
(t/deftest ac-interpolate-color
|
|
||||||
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
|
||||||
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}]
|
|
||||||
;; At c1's offset → c1 with updated offset
|
|
||||||
(let [result (c/interpolate-color c1 c2 0.0)]
|
|
||||||
(t/is (= "#000000" (:color result)))
|
|
||||||
(t/is (= 0.0 (:opacity result))))
|
|
||||||
;; At c2's offset → c2 with updated offset
|
|
||||||
(let [result (c/interpolate-color c1 c2 1.0)]
|
|
||||||
(t/is (= "#ffffff" (:color result)))
|
|
||||||
(t/is (= 1.0 (:opacity result))))
|
|
||||||
;; At midpoint → gray
|
|
||||||
(let [result (c/interpolate-color c1 c2 0.5)]
|
|
||||||
(t/is (= "#7f7f7f" (:color result)))
|
|
||||||
(t/is (mth/close? (:opacity result) 0.5)))))
|
|
||||||
|
|
||||||
(t/deftest ac-uniform-spread
|
|
||||||
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
|
||||||
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
|
||||||
stops (c/uniform-spread c1 c2 3)]
|
|
||||||
(t/is (= 3 (count stops)))
|
|
||||||
(t/is (= 0.0 (:offset (first stops))))
|
|
||||||
(t/is (mth/close? 0.5 (:offset (second stops))))
|
|
||||||
(t/is (= 1.0 (:offset (last stops))))))
|
|
||||||
|
|
||||||
(t/deftest ac-uniform-spread?
|
|
||||||
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
|
||||||
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
|
||||||
stops (c/uniform-spread c1 c2 3)]
|
|
||||||
;; A uniformly spread result should pass the predicate
|
|
||||||
(t/is (true? (c/uniform-spread? stops))))
|
|
||||||
;; Manual non-uniform stops should not pass
|
|
||||||
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
|
||||||
{:color "#888888" :opacity 0.5 :offset 0.3}
|
|
||||||
{:color "#ffffff" :opacity 1.0 :offset 1.0}]]
|
|
||||||
(t/is (false? (c/uniform-spread? stops)))))
|
|
||||||
|
|
||||||
(t/deftest ac-interpolate-gradient
|
|
||||||
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
|
||||||
{:color "#ffffff" :opacity 1.0 :offset 1.0}]]
|
|
||||||
;; At start
|
|
||||||
(let [result (c/interpolate-gradient stops 0.0)]
|
|
||||||
(t/is (= "#000000" (:color result))))
|
|
||||||
;; At end
|
|
||||||
(let [result (c/interpolate-gradient stops 1.0)]
|
|
||||||
(t/is (= "#ffffff" (:color result))))
|
|
||||||
;; In the middle
|
|
||||||
(let [result (c/interpolate-gradient stops 0.5)]
|
|
||||||
(t/is (= "#7f7f7f" (:color result))))))
|
|
||||||
|
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
[common-tests.text-test]
|
[common-tests.text-test]
|
||||||
[common-tests.time-test]
|
[common-tests.time-test]
|
||||||
[common-tests.types.absorb-assets-test]
|
[common-tests.types.absorb-assets-test]
|
||||||
|
[common-tests.types.color-test]
|
||||||
[common-tests.types.components-test]
|
[common-tests.types.components-test]
|
||||||
[common-tests.types.container-test]
|
[common-tests.types.container-test]
|
||||||
[common-tests.types.fill-test]
|
[common-tests.types.fill-test]
|
||||||
@ -126,6 +127,7 @@
|
|||||||
'common-tests.text-test
|
'common-tests.text-test
|
||||||
'common-tests.time-test
|
'common-tests.time-test
|
||||||
'common-tests.types.absorb-assets-test
|
'common-tests.types.absorb-assets-test
|
||||||
|
'common-tests.types.color-test
|
||||||
'common-tests.types.components-test
|
'common-tests.types.components-test
|
||||||
'common-tests.types.container-test
|
'common-tests.types.container-test
|
||||||
'common-tests.types.fill-test
|
'common-tests.types.fill-test
|
||||||
|
|||||||
166
common/test/common_tests/types/color_test.cljc
Normal file
166
common/test/common_tests/types/color_test.cljc
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
;; 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) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns common-tests.types.color-test
|
||||||
|
(:require
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.common.types.color :as colors]
|
||||||
|
[clojure.test :as t]))
|
||||||
|
|
||||||
|
;; --- Predicates
|
||||||
|
|
||||||
|
(t/deftest valid-hex-color
|
||||||
|
(t/is (false? (colors/valid-hex-color? nil)))
|
||||||
|
(t/is (false? (colors/valid-hex-color? "")))
|
||||||
|
(t/is (false? (colors/valid-hex-color? "#")))
|
||||||
|
(t/is (false? (colors/valid-hex-color? "#qqqqqq")))
|
||||||
|
(t/is (true? (colors/valid-hex-color? "#aaa")))
|
||||||
|
(t/is (false? (colors/valid-hex-color? "#aaaa")))
|
||||||
|
(t/is (true? (colors/valid-hex-color? "#fabada"))))
|
||||||
|
|
||||||
|
(t/deftest valid-rgb-color
|
||||||
|
(t/is (false? (colors/valid-rgb-color? nil)))
|
||||||
|
(t/is (false? (colors/valid-rgb-color? "")))
|
||||||
|
(t/is (false? (colors/valid-rgb-color? "()")))
|
||||||
|
(t/is (true? (colors/valid-rgb-color? "(255, 30, 30)")))
|
||||||
|
(t/is (true? (colors/valid-rgb-color? "rgb(255, 30, 30)"))))
|
||||||
|
|
||||||
|
;; --- Conversions
|
||||||
|
|
||||||
|
(t/deftest rgb-to-str
|
||||||
|
(t/is (= "rgb(1,2,3)" (colors/rgb->str [1 2 3])))
|
||||||
|
(t/is (= "rgba(1,2,3,4)" (colors/rgb->str [1 2 3 4]))))
|
||||||
|
|
||||||
|
(t/deftest rgb-to-hsv
|
||||||
|
(t/is (= [210.0 0.6666666666666666 3.0] (colors/rgb->hsv [1.0 2.0 3.0]))))
|
||||||
|
|
||||||
|
(t/deftest hsv-to-rgb
|
||||||
|
(t/is (= [1 2 3]
|
||||||
|
(colors/hsv->rgb [210 0.6666666666666666 3]))))
|
||||||
|
|
||||||
|
(t/deftest rgb-to-hex
|
||||||
|
(t/is (= "#010203" (colors/rgb->hex [1 2 3]))))
|
||||||
|
|
||||||
|
(t/deftest hex-to-rgb
|
||||||
|
(t/is (= [0 0 0] (colors/hex->rgb "#kkk")))
|
||||||
|
(t/is (= [1 2 3] (colors/hex->rgb "#010203"))))
|
||||||
|
|
||||||
|
(t/deftest format-hsla
|
||||||
|
(t/is (= "210, 50%, 0.78%, 1" (colors/format-hsla [210.0 0.5 0.00784313725490196 1])))
|
||||||
|
(t/is (= "220, 5%, 30%, 0.8" (colors/format-hsla [220.0 0.05 0.3 0.8]))))
|
||||||
|
|
||||||
|
(t/deftest format-rgba
|
||||||
|
(t/is (= "210, 199, 12, 0.08" (colors/format-rgba [210 199 12 0.08])))
|
||||||
|
(t/is (= "210, 199, 12, 1" (colors/format-rgba [210 199 12 1]))))
|
||||||
|
|
||||||
|
(t/deftest rgb-to-hsl
|
||||||
|
(t/is (= [210.0 0.5 0.00784313725490196] (colors/rgb->hsl [1 2 3]))))
|
||||||
|
|
||||||
|
(t/deftest hsl-to-rgb
|
||||||
|
(t/is (= [1 2 3] (colors/hsl->rgb [210.0 0.5 0.00784313725490196])))
|
||||||
|
(t/is (= [210.0 0.5 0.00784313725490196] (colors/rgb->hsl [1 2 3]))))
|
||||||
|
|
||||||
|
(t/deftest expand-hex
|
||||||
|
(t/is (= "aaaaaa" (colors/expand-hex "a")))
|
||||||
|
(t/is (= "aaaaaa" (colors/expand-hex "aa")))
|
||||||
|
(t/is (= "aaaaaa" (colors/expand-hex "aaa")))
|
||||||
|
(t/is (= "aaaa" (colors/expand-hex "aaaa"))))
|
||||||
|
|
||||||
|
(t/deftest prepend-hash
|
||||||
|
(t/is "#aaa" (colors/prepend-hash "aaa"))
|
||||||
|
(t/is "#aaa" (colors/prepend-hash "#aaa")))
|
||||||
|
|
||||||
|
(t/deftest remove-hash
|
||||||
|
(t/is "aaa" (colors/remove-hash "aaa"))
|
||||||
|
(t/is "aaa" (colors/remove-hash "#aaa")))
|
||||||
|
|
||||||
|
(t/deftest color-string-pred
|
||||||
|
(t/is (true? (colors/color-string? "#aaa")))
|
||||||
|
(t/is (true? (colors/color-string? "(10,10,10)")))
|
||||||
|
(t/is (true? (colors/color-string? "rgb(10,10,10)")))
|
||||||
|
(t/is (true? (colors/color-string? "magenta")))
|
||||||
|
(t/is (false? (colors/color-string? nil)))
|
||||||
|
(t/is (false? (colors/color-string? "")))
|
||||||
|
(t/is (false? (colors/color-string? "kkkkkk"))))
|
||||||
|
|
||||||
|
;; --- Gradient helpers
|
||||||
|
|
||||||
|
(t/deftest interpolate-color
|
||||||
|
(t/testing "at c1 offset returns c1 color"
|
||||||
|
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
||||||
|
result (colors/interpolate-color c1 c2 0.0)]
|
||||||
|
(t/is (= "#000000" (:color result)))
|
||||||
|
(t/is (= 0.0 (:opacity result)))))
|
||||||
|
(t/testing "at c2 offset returns c2 color"
|
||||||
|
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
||||||
|
result (colors/interpolate-color c1 c2 1.0)]
|
||||||
|
(t/is (= "#ffffff" (:color result)))
|
||||||
|
(t/is (= 1.0 (:opacity result)))))
|
||||||
|
(t/testing "at midpoint returns interpolated gray"
|
||||||
|
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
||||||
|
result (colors/interpolate-color c1 c2 0.5)]
|
||||||
|
(t/is (= "#7f7f7f" (:color result)))
|
||||||
|
(t/is (mth/close? (:opacity result) 0.5)))))
|
||||||
|
|
||||||
|
(t/deftest uniform-spread
|
||||||
|
(t/testing "produces correct count and offsets"
|
||||||
|
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
||||||
|
stops (colors/uniform-spread c1 c2 3)]
|
||||||
|
(t/is (= 3 (count stops)))
|
||||||
|
(t/is (= 0.0 (:offset (first stops))))
|
||||||
|
(t/is (mth/close? 0.5 (:offset (second stops))))
|
||||||
|
(t/is (= 1.0 (:offset (last stops))))))
|
||||||
|
(t/testing "single stop returns a vector of one element (no division by zero)"
|
||||||
|
(let [c1 {:color "#ff0000" :opacity 1.0 :offset 0.0}
|
||||||
|
stops (colors/uniform-spread c1 c1 1)]
|
||||||
|
(t/is (= 1 (count stops))))))
|
||||||
|
|
||||||
|
(t/deftest uniform-spread?
|
||||||
|
(t/testing "uniformly spread stops are detected as uniform"
|
||||||
|
(let [c1 {:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
c2 {:color "#ffffff" :opacity 1.0 :offset 1.0}
|
||||||
|
stops (colors/uniform-spread c1 c2 3)]
|
||||||
|
(t/is (true? (colors/uniform-spread? stops)))))
|
||||||
|
(t/testing "two-stop gradient is uniform by definition"
|
||||||
|
(let [stops [{:color "#ff0000" :opacity 1.0 :offset 0.0}
|
||||||
|
{:color "#0000ff" :opacity 1.0 :offset 1.0}]]
|
||||||
|
(t/is (true? (colors/uniform-spread? stops)))))
|
||||||
|
(t/testing "stops with wrong offset are not uniform"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#888888" :opacity 0.5 :offset 0.3}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 1.0}]]
|
||||||
|
(t/is (false? (colors/uniform-spread? stops)))))
|
||||||
|
(t/testing "stops with correct offset but wrong color are not uniform"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#aaaaaa" :opacity 0.5 :offset 0.5}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 1.0}]]
|
||||||
|
(t/is (false? (colors/uniform-spread? stops))))))
|
||||||
|
|
||||||
|
(t/deftest interpolate-gradient
|
||||||
|
(t/testing "at start offset returns first stop color"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 1.0}]
|
||||||
|
result (colors/interpolate-gradient stops 0.0)]
|
||||||
|
(t/is (= "#000000" (:color result)))))
|
||||||
|
(t/testing "at end offset returns last stop color"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 1.0}]
|
||||||
|
result (colors/interpolate-gradient stops 1.0)]
|
||||||
|
(t/is (= "#ffffff" (:color result)))))
|
||||||
|
(t/testing "at midpoint returns interpolated gray"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 1.0}]
|
||||||
|
result (colors/interpolate-gradient stops 0.5)]
|
||||||
|
(t/is (= "#7f7f7f" (:color result)))))
|
||||||
|
(t/testing "offset beyond last stop returns last stop color (nil idx guard)"
|
||||||
|
(let [stops [{:color "#000000" :opacity 0.0 :offset 0.0}
|
||||||
|
{:color "#ffffff" :opacity 1.0 :offset 0.5}]
|
||||||
|
result (colors/interpolate-gradient stops 1.0)]
|
||||||
|
(t/is (= "#ffffff" (:color result))))))
|
||||||
@ -207,3 +207,18 @@
|
|||||||
fill1 (nth fills1 1)]
|
fill1 (nth fills1 1)]
|
||||||
(t/is (nil? fill1))
|
(t/is (nil? fill1))
|
||||||
(t/is (equivalent-fill? fill0 sample-fill-6))))
|
(t/is (equivalent-fill? fill0 sample-fill-6))))
|
||||||
|
|
||||||
|
(t/deftest indexed-access-with-default
|
||||||
|
(t/testing "nth with default returns fill for valid index"
|
||||||
|
;; Regression: CLJS -nth with default had reversed d/in-range? args,
|
||||||
|
;; so it always fell through to the default even for valid indices.
|
||||||
|
(let [fills (types.fills/from-plain [sample-fill-6])
|
||||||
|
sentinel ::not-found
|
||||||
|
result (nth fills 0 sentinel)]
|
||||||
|
(t/is (not= sentinel result))
|
||||||
|
(t/is (equivalent-fill? result sample-fill-6))))
|
||||||
|
(t/testing "nth with default returns default for out-of-range index"
|
||||||
|
(let [fills (types.fills/from-plain [sample-fill-6])
|
||||||
|
sentinel ::not-found]
|
||||||
|
(t/is (= sentinel (nth fills 1 sentinel)))
|
||||||
|
(t/is (= sentinel (nth fills -1 sentinel))))))
|
||||||
|
|||||||
@ -973,6 +973,31 @@
|
|||||||
(t/is (mth/close? 10.0 (:x2 rect) 0.1))
|
(t/is (mth/close? 10.0 (:x2 rect) 0.1))
|
||||||
(t/is (mth/close? 10.0 (:y2 rect) 0.1))))
|
(t/is (mth/close? 10.0 (:y2 rect) 0.1))))
|
||||||
|
|
||||||
|
(t/deftest segment-content->selrect-multi-line
|
||||||
|
;; Regression: calculate-extremities used move-p instead of from-p in
|
||||||
|
;; the :line-to branch. For a subpath with multiple consecutive line-to
|
||||||
|
;; commands, the selrect must still match the reference implementation.
|
||||||
|
(let [;; A subpath that starts away from the origin and has three
|
||||||
|
;; line-to segments so that move-p diverges from from-p for the
|
||||||
|
;; later segments.
|
||||||
|
segments [{:command :move-to :params {:x 5.0 :y 5.0}}
|
||||||
|
{:command :line-to :params {:x 15.0 :y 0.0}}
|
||||||
|
{:command :line-to :params {:x 20.0 :y 8.0}}
|
||||||
|
{:command :line-to :params {:x 10.0 :y 12.0}}]
|
||||||
|
content (path/content segments)
|
||||||
|
rect (path.segment/content->selrect content)
|
||||||
|
ref-pts (calculate-extremities segments)]
|
||||||
|
|
||||||
|
;; Bounding box must enclose all four vertices exactly.
|
||||||
|
(t/is (some? rect))
|
||||||
|
(t/is (mth/close? 5.0 (:x1 rect) 0.1))
|
||||||
|
(t/is (mth/close? 0.0 (:y1 rect) 0.1))
|
||||||
|
(t/is (mth/close? 20.0 (:x2 rect) 0.1))
|
||||||
|
(t/is (mth/close? 12.0 (:y2 rect) 0.1))
|
||||||
|
|
||||||
|
;; Must agree with the reference implementation.
|
||||||
|
(t/is (= ref-pts (calculate-extremities content)))))
|
||||||
|
|
||||||
(t/deftest segment-content-center
|
(t/deftest segment-content-center
|
||||||
(let [content (path/content sample-content-square)
|
(let [content (path/content sample-content-square)
|
||||||
center (path.segment/content-center content)]
|
center (path.segment/content-center content)]
|
||||||
|
|||||||
@ -186,13 +186,9 @@
|
|||||||
flex (make-flex-frame :parent-id root-id)
|
flex (make-flex-frame :parent-id root-id)
|
||||||
child (make-shape :parent-id (:id flex))]
|
child (make-shape :parent-id (:id flex))]
|
||||||
|
|
||||||
;; Note: inside-layout? calls (cfh/frame-shape? current-id) with a UUID id,
|
(t/testing "returns true when child is under a flex frame"
|
||||||
;; but frame-shape? checks (:type uuid) which is nil for a UUID value.
|
|
||||||
;; The function therefore always returns false regardless of structure.
|
|
||||||
;; These tests document the actual (not the intended) behavior.
|
|
||||||
(t/testing "returns false when child is under a flex frame"
|
|
||||||
(let [objects {root-id root (:id flex) flex (:id child) child}]
|
(let [objects {root-id root (:id flex) flex (:id child) child}]
|
||||||
(t/is (not (layout/inside-layout? objects child)))))
|
(t/is (layout/inside-layout? objects child))))
|
||||||
|
|
||||||
(t/testing "returns false for root shape"
|
(t/testing "returns false for root shape"
|
||||||
(let [objects {root-id root (:id flex) flex (:id child) child}]
|
(let [objects {root-id root (:id flex) flex (:id child) child}]
|
||||||
|
|||||||
@ -1918,7 +1918,7 @@
|
|||||||
(let [token (ctob/get-token-by-name lib "shadow-test" "test.shadow-with-type")]
|
(let [token (ctob/get-token-by-name lib "shadow-test" "test.shadow-with-type")]
|
||||||
(t/is (some? token))
|
(t/is (some? token))
|
||||||
(t/is (= :shadow (:type token)))
|
(t/is (= :shadow (:type token)))
|
||||||
(t/is (= [{:offset-x "0", :offset-y "4px", :blur "8px", :spread "0", :color "rgba(0,0,0,0.2)", :inset false}]
|
(t/is (= [{:offset-x "0", :offset-y "4px", :blur "8px", :spread "0", :color "rgba(0,0,0,0.2)", :inset true}]
|
||||||
(:value token)))))
|
(:value token)))))
|
||||||
|
|
||||||
(t/testing "shadow token with description"
|
(t/testing "shadow token with description"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user