🎉 Cap stop amount in UI for wasm (#6438)

* 🎉 Cap in the colorpicker the amount of stops a gradient can have

* 🎉 Cap the stops amount in gradient handlers

* 🎉 Disable add stop in gradient handlers (viewport + colorpicker)

*  Add integration test for gradient limits

* 💄 Address PR suggestion
This commit is contained in:
Belén Albeza 2025-05-13 10:37:05 +02:00 committed by GitHub
parent 69cc4fb4c2
commit 91fbe8f8ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 438 additions and 102 deletions

View file

@ -761,3 +761,5 @@
(d/patch-object (select-keys props basic-extract-props))
(cond-> (cfh/text-shape? shape) (patch-text-props props))
(cond-> (cfh/frame-shape? shape) (patch-layout-props props)))))
(def MAX-GRADIENT-STOPS 16)

View file

@ -0,0 +1,279 @@
{
"~:id": "~u66697432-c33d-8055-8006-2c62de27d653",
"~:file-id": "~u66697432-c33d-8055-8006-2c62cc084cac",
"~:created-at": "~m1747053122715",
"~:data": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0.0,
"~:y": 0.0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0.0,
"~:y": 0.01
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1.0,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~u4a1d24c0-c794-80ff-8006-2c62cd7f23e0"
]
}
},
"~u4a1d24c0-c794-80ff-8006-2c62cd7f23e0": {
"~#shape": {
"~:y": 303,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:rotation": 0,
"~:hide-in-viewer": false,
"~:name": "Rectangle",
"~:width": 100,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 205,
"~:y": 303
}
},
{
"~#point": {
"~:x": 305,
"~:y": 303
}
},
{
"~#point": {
"~:x": 305,
"~:y": 403
}
},
{
"~#point": {
"~:x": 205,
"~:y": 403
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1.0,
"~:b": 0.0,
"~:c": 0.0,
"~:d": 1.0,
"~:e": 0.0,
"~:f": 0.0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~u4a1d24c0-c794-80ff-8006-2c62cd7f23e0",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 205,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 205,
"~:y": 303,
"~:width": 100,
"~:height": 100,
"~:x1": 205,
"~:y1": 303,
"~:x2": 305,
"~:y2": 403
}
},
"~:fills": [
{
"~:fill-color-gradient": {
"~:start-x": 0.5,
"~:start-y": 0,
"~:end-x": 0.5,
"~:end-y": 1,
"~:width": 1,
"~:type": "~:linear",
"~:stops": [
{
"~:color": "#003fff",
"~:offset": 0,
"~:opacity": 1
},
{
"~:color": "#003fff",
"~:offset": 0.07,
"~:opacity": 0.9299999999999999
},
{
"~:color": "#003fff",
"~:offset": 0.13,
"~:opacity": 0.87
},
{
"~:color": "#003fff",
"~:offset": 0.2,
"~:opacity": 0.8
},
{
"~:color": "#003fff",
"~:offset": 0.27,
"~:opacity": 0.73
},
{
"~:color": "#003fff",
"~:offset": 0.33,
"~:opacity": 0.6699999999999999
},
{
"~:color": "#003fff",
"~:offset": 0.4,
"~:opacity": 0.6
},
{
"~:color": "#003fff",
"~:offset": 0.47,
"~:opacity": 0.53
},
{
"~:color": "#003fff",
"~:offset": 0.53,
"~:opacity": 0.47
},
{
"~:color": "#003fff",
"~:offset": 0.6,
"~:opacity": 0.4
},
{
"~:color": "#003fff",
"~:offset": 0.67,
"~:opacity": 0.32999999999999996
},
{
"~:color": "#003fff",
"~:offset": 0.73,
"~:opacity": 0.27
},
{
"~:color": "#003fff",
"~:offset": 0.8,
"~:opacity": 0.19999999999999996
},
{
"~:color": "#003fff",
"~:offset": 0.87,
"~:opacity": 0.13
},
{
"~:color": "#003fff",
"~:offset": 0.93,
"~:opacity": 0.06999999999999995
},
{
"~:color": "#003fff",
"~:offset": 1,
"~:opacity": 0
}
]
}
}
],
"~:flip-x": null,
"~:height": 100,
"~:flip-y": null
}
}
},
"~:id": "~u66697432-c33d-8055-8006-2c62cc084cad",
"~:name": "Page 1"
}
}

View file

@ -36,6 +36,17 @@ export class BasePage {
async mockRPC(path, jsonFilename, options) {
return BasePage.mockRPC(this.page, path, jsonFilename, options);
}
async mockConfigFlags(flags) {
const url = "**/js/config.js?ts=*";
return await this.page.route(url, (route) =>
route.fulfill({
status: 200,
contentType: "application/javascript",
body: `var penpotFlags = "${flags.join(" ")}";`,
}),
);
}
}
export default BasePage;

View file

@ -49,6 +49,12 @@ export class WorkspacePage extends BaseWebSocketPage {
"get-profiles-for-file-comments?file-id=*",
"workspace/get-profile-for-file-comments.json",
);
await BaseWebSocketPage.mockRPC(
page,
"update-profile-props",
"workspace/update-profile-empty.json",
);
}
static anyTeamId = "c7ce0794-0992-8105-8004-38e630f7920a";

View file

@ -43,48 +43,44 @@ test("Create a LINEAR gradient", async ({ page }) => {
await workspacePage.clickLeafLayer("Rectangle");
const swatch = workspacePage.page.getByRole("button", { name: "#B1B2B5" });
const swatchBox = await swatch.boundingBox();
await swatch.click();
const select = await workspacePage.page.getByText("Solid");
const select = workspacePage.page.getByText("Solid");
await select.click();
const gradOption = await workspacePage.page.getByText("Gradient");
const gradOption = workspacePage.page.getByText("Gradient");
await gradOption.click();
const addStopBtn = await workspacePage.page.getByRole("button", {
const addStopBtn = workspacePage.page.getByRole("button", {
name: "Add stop",
});
await addStopBtn.click();
await addStopBtn.click();
await addStopBtn.click();
const removeBtn = await workspacePage.page
.getByTestId("colorpicker")
const removeBtn = workspacePage.colorpicker
.getByRole("button", { name: "Remove color" })
.nth(2);
await removeBtn.click();
await removeBtn.click();
const inputColor1 = await workspacePage.page.getByPlaceholder("Mixed").nth(1);
const inputColor1 = workspacePage.colorpicker
.getByPlaceholder("Mixed")
.nth(1);
await inputColor1.fill("fabada");
const inputOpacity1 = await workspacePage.page
.getByTestId("colorpicker")
.getByPlaceholder("--")
.nth(1);
const inputOpacity1 = workspacePage.colorpicker.getByPlaceholder("--").nth(1);
await inputOpacity1.fill("100");
const inputColor2 = await workspacePage.page.getByPlaceholder("Mixed").nth(2);
const inputColor2 = workspacePage.colorpicker
.getByPlaceholder("Mixed")
.nth(1);
await inputColor2.fill("red");
const inputOpacity2 = await workspacePage.page
.getByTestId("colorpicker")
.getByPlaceholder("--")
.nth(2);
const inputOpacity2 = workspacePage.colorpicker.getByPlaceholder("--").nth(1);
await inputOpacity2.fill("100");
const inputOpacityGlobal = await workspacePage.page
const inputOpacityGlobal = workspacePage.page
.locator("div")
.filter({ hasText: /^FillLinear gradient%$/ })
.getByPlaceholder("--");
@ -110,60 +106,75 @@ test("Create a RADIAL gradient", async ({ page }) => {
await workspacePage.clickLeafLayer("Rectangle");
const swatch = workspacePage.page.getByRole("button", { name: "#B1B2B5" });
const swatchBox = await swatch.boundingBox();
await swatch.click();
const select = await workspacePage.page.getByText("Solid");
const select = workspacePage.page.getByText("Solid");
await select.click();
const gradOption = await workspacePage.page.getByText("Gradient");
const gradOption = workspacePage.page.getByText("Gradient");
await gradOption.click();
const gradTypeOptions = await workspacePage.page
.getByTestId("colorpicker")
const gradTypeOptions = workspacePage.colorpicker
.locator("div")
.filter({ hasText: "Linear" })
.nth(3);
await gradTypeOptions.click();
const gradRadialOption = await workspacePage.page
const gradRadialOption = workspacePage.page
.locator("li")
.filter({ hasText: "Radial" });
await gradRadialOption.click();
const addStopBtn = await workspacePage.page.getByRole("button", {
const addStopBtn = workspacePage.page.getByRole("button", {
name: "Add stop",
});
await addStopBtn.click();
await addStopBtn.click();
await addStopBtn.click();
const removeBtn = await workspacePage.page
.getByTestId("colorpicker")
const removeBtn = workspacePage.colorpicker
.getByRole("button", { name: "Remove color" })
.nth(2);
await removeBtn.click();
await removeBtn.click();
const inputColor1 = await workspacePage.page.getByPlaceholder("Mixed").nth(1);
const inputColor1 = workspacePage.page.getByPlaceholder("Mixed").nth(1);
await inputColor1.fill("fabada");
const inputOpacity1 = await workspacePage.page
.getByTestId("colorpicker")
.getByPlaceholder("--")
.nth(1);
const inputOpacity1 = workspacePage.colorpicker.getByPlaceholder("--").nth(1);
await inputOpacity1.fill("100");
const inputColor2 = await workspacePage.page.getByPlaceholder("Mixed").nth(2);
const inputColor2 = workspacePage.page.getByPlaceholder("Mixed").nth(2);
await inputColor2.fill("red");
const inputOpacity2 = await workspacePage.page
.getByTestId("colorpicker")
.getByPlaceholder("--")
.nth(2);
const inputOpacity2 = workspacePage.colorpicker.getByPlaceholder("--").nth(1);
await inputOpacity2.fill("100");
});
test("Gradient stops limit", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.mockConfigFlags(["enable-binary-fills"]);
await workspacePage.setupEmptyFile(page);
await workspacePage.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"workspace/get-file-fragment-gradient-limits.json",
);
await workspacePage.goToWorkspace();
await workspacePage.clickLeafLayer("Rectangle");
const swatch = workspacePage.page.getByRole("button", {
name: "Linear gradient",
});
await swatch.click();
await expect(workspacePage.colorpicker).toBeVisible();
await expect(
workspacePage.colorpicker.getByRole("button", { name: "Add stop" }),
).toBeDisabled();
});
// Fix for https://tree.taiga.io/project/penpot/issue/9900
test("Bug 9900 - Color picker has no inputs for HSV values", async ({
page,
@ -203,7 +214,6 @@ test("Bug 10089 - Cannot change alpha", async ({ page }) => {
await workspacePage.clickLeafLayer("Rectangle");
const swatch = workspacePage.page.getByRole("button", { name: "#B1B2B5" });
const swatchBox = await swatch.boundingBox();
await swatch.click();
const alpha = workspacePage.page.getByLabel("A", { exact: true });

View file

@ -13,8 +13,9 @@
[app.common.schema :as sm]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.shape :refer [check-stroke]]
[app.common.types.shape :as shp]
[app.common.types.shape.shadow :refer [check-shadow]]
[app.config :as cfg]
[app.main.broadcast :as mbc]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
@ -24,6 +25,7 @@
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.util.storage :as storage]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
@ -421,7 +423,7 @@
[ids stroke]
(assert
(check-stroke stroke)
(shp/check-stroke stroke)
"expected a valid stroke struct")
(assert
@ -821,39 +823,43 @@
(update [_ state]
(update state :colorpicker
(fn [{:keys [stops editing-stop] :as state}]
(if (cc/uniform-spread? stops)
;; Add to uniform
(let [stops (->> (cc/uniform-spread (first stops) (last stops) (inc (count stops)))
(mapv split-color-components))]
(-> state
(assoc :current-color (get stops editing-stop))
(assoc :stops stops)))
(let [cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
can-add-stop? (or (not cap-stops?) (< (count stops) shp/MAX-GRADIENT-STOPS))]
(if can-add-stop?
(if (cc/uniform-spread? stops)
;; Add to uniform
(let [stops (->> (cc/uniform-spread (first stops) (last stops) (inc (count stops)))
(mapv split-color-components))]
(-> state
(assoc :current-color (get stops editing-stop))
(assoc :stops stops)))
;; We add the stop to the middle point between the selected
;; and the next one.
;; If the last stop is selected then it's added between the
;; last two stops.
(let [index
(if (= editing-stop (dec (count stops)))
(dec editing-stop)
editing-stop)
;; We add the stop to the middle point between the selected
;; and the next one.
;; If the last stop is selected then it's added between the
;; last two stops.
(let [index
(if (= editing-stop (dec (count stops)))
(dec editing-stop)
editing-stop)
{from-offset :offset} (get stops index)
{to-offset :offset} (get stops (inc index))
{from-offset :offset} (get stops index)
{to-offset :offset} (get stops (inc index))
half-point-offset
(+ from-offset (/ (- to-offset from-offset) 2))
half-point-offset
(+ from-offset (/ (- to-offset from-offset) 2))
new-stop (-> (cc/interpolate-gradient stops half-point-offset)
(split-color-components))
new-stop (-> (cc/interpolate-gradient stops half-point-offset)
(split-color-components))
stops (conj stops new-stop)
stops (into [] (sort-by :offset stops))
editing-stop (d/index-of-pred stops #(= new-stop %))]
(-> state
(assoc :editing-stop editing-stop)
(assoc :current-color (get stops editing-stop))
(assoc :stops stops)))))))))
stops (conj stops new-stop)
stops (into [] (sort-by :offset stops))
editing-stop (d/index-of-pred stops #(= new-stop %))]
(-> state
(assoc :editing-stop editing-stop)
(assoc :current-color (get stops editing-stop))
(assoc :stops stops))))
state)))))))
(defn update-colorpicker-add-stop
[offset]
@ -863,15 +869,18 @@
(update state :colorpicker
(fn [state]
(let [stops (:stops state)
new-stop (-> (cc/interpolate-gradient stops offset)
(split-color-components))
stops (conj stops new-stop)
stops (into [] (sort-by :offset stops))
editing-stop (d/index-of-pred stops #(= new-stop %))]
(-> state
(assoc :editing-stop editing-stop)
(assoc :current-color (get stops editing-stop))
(assoc :stops stops))))))))
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
can-add-stop? (or (not cap-stops?) (< (count stops) shp/MAX-GRADIENT-STOPS))]
(if can-add-stop? (let [new-stop (-> (cc/interpolate-gradient stops offset)
(split-color-components))
stops (conj stops new-stop)
stops (into [] (sort-by :offset stops))
editing-stop (d/index-of-pred stops #(= new-stop %))]
(-> state
(assoc :editing-stop editing-stop)
(assoc :current-color (get stops editing-stop))
(assoc :stops stops)))
state)))))))
(defn update-colorpicker-stops
[stops]
@ -881,7 +890,8 @@
(update state :colorpicker
(fn [state]
(let [stop (or (:editing-stop state) 0)
stops (mapv split-color-components stops)]
cap-stops? (or (features/active-feature? state "render-wasm/v1") (contains? cfg/flags :binary-fills))
stops (mapv split-color-components (if cap-stops? (take shp/MAX-GRADIENT-STOPS stops) stops))]
(-> state
(assoc :current-color (get stops stop))
(assoc :stops stops))))))))

View file

@ -12,6 +12,7 @@
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.types.shape :as shp]
[app.config :as cfg]
[app.main.data.event :as-alias ev]
[app.main.data.modal :as modal]
@ -20,6 +21,7 @@
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.media :as dwm]
[app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.file-uploader :refer [file-uploader]]
@ -336,6 +338,8 @@
(fn [value]
(st/emit! (dc/update-colorpicker-gradient-opacity (/ value 100)))))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
tabs
#js [#js {:aria-label (tr "workspace.libraries.colors.rgba")
:icon ic/rgba
@ -435,7 +439,7 @@
(when (= selected-mode :gradient)
[:> gradients*
{:type (:type state)
:stops (:stops state)
:stops (if cap-stops? (vec (take shp/MAX-GRADIENT-STOPS (:stops state))) (:stops state))
:editing-stop (:editing-stop state)
:on-stop-edit-start handle-stop-edit-start
:on-stop-edit-finish handle-stop-edit-finish

View file

@ -11,6 +11,9 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.math :as mth]
[app.common.types.shape :as shp]
[app.config :as cfg]
[app.main.features :as features]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.components.reorder-handler :refer [reorder-handler]]
[app.main.ui.components.select :refer [select]]
@ -283,7 +286,9 @@
(mf/deps on-reverse-stops)
(fn []
(when on-reverse-stops
(on-reverse-stops))))]
(on-reverse-stops))))
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
add-stop-disabled? (when cap-stops? (>= (count stops) shp/MAX-GRADIENT-STOPS))]
[:div {:class (stl/css :gradient-panel)}
[:div {:class (stl/css :gradient-preview)}
@ -294,9 +299,10 @@
:on-pointer-leave handle-preview-leave
:on-pointer-move handle-preview-move
:on-pointer-down handle-preview-down}
[:div {:class (stl/css :gradient-preview-stop-preview)
:style {:display (if (:hover? @preview-state) "block" "none")
"--preview-position" (dm/str (* 100 (:offset @preview-state)) "%")}}]]
(when (not add-stop-disabled?)
[:div {:class (stl/css :gradient-preview-stop-preview)
:style {:display (if (:hover? @preview-state) "block" "none")
"--preview-position" (dm/str (* 100 (:offset @preview-state)) "%")}}])]
[:div {:class (stl/css :gradient-preview-stop-wrapper)}
(for [[index {:keys [color offset r g b alpha]}] (d/enumerate stops)]
@ -339,6 +345,7 @@
:icon "switch"}]
[:> icon-button* {:variant "ghost"
:aria-label "Add stop"
:disabled add-stop-disabled?
:on-click handle-add-stop
:icon "add"}]]]

View file

@ -15,7 +15,10 @@
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.points :as gsp]
[app.common.math :as mth]
[app.common.types.shape :as shp]
[app.config :as cfg]
[app.main.data.workspace.colors :as dc]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.workspace.viewport.viewport-ref :as uwvv]
@ -131,6 +134,9 @@
handler-state (mf/use-state {:display? false :offset 0 :hover nil})
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
can-add-stop? (if cap-stops? (< (count stops) shp/MAX-GRADIENT-STOPS) true)
endpoint-on-pointer-down
(fn [position event]
(dom/stop-propagation event)
@ -164,7 +170,8 @@
points-on-pointer-enter
(mf/use-fn
(fn []
(swap! handler-state assoc :display? true)))
(when can-add-stop?
(swap! handler-state assoc :display? true))))
points-on-pointer-leave
(mf/use-fn
@ -177,17 +184,17 @@
(fn [e]
(dom/prevent-default e)
(dom/stop-propagation e)
(let [raw-pt (dom/get-client-position e)
position (uwvv/point->viewport raw-pt)
lv (-> (gpt/to-vec from-p to-p) (gpt/unit))
nv (gpt/normal-left lv)
offset (-> (gsp/project-t position [from-p to-p] nv)
(mth/precision 2))
new-stop (cc/interpolate-gradient stops offset)
stops (conj stops new-stop)
stops (->> stops (sort-by :offset) (into []))]
(st/emit! (dc/update-colorpicker-stops stops)))))
(when can-add-stop?
(let [raw-pt (dom/get-client-position e)
position (uwvv/point->viewport raw-pt)
lv (-> (gpt/to-vec from-p to-p) (gpt/unit))
nv (gpt/normal-left lv)
offset (-> (gsp/project-t position [from-p to-p] nv)
(mth/precision 2))
new-stop (cc/interpolate-gradient stops offset)
stops (conj stops new-stop)
stops (->> stops (sort-by :offset) (into []))]
(st/emit! (dc/update-colorpicker-stops stops))))))
points-on-pointer-move
(mf/use-fn
@ -354,7 +361,7 @@
:cx (:x width-p)
:cy (:y width-p)
:r (/ gradient-width-handler-radius-handler zoom)
:fill "transpgarent"
:fill "transparent"
:on-pointer-down (partial endpoint-on-pointer-down :width-p)
:on-pointer-enter (partial endpoint-on-pointer-enter :width-p)
:on-pointer-leave (partial endpoint-on-pointer-leave :width-p)
@ -518,7 +525,10 @@
shape (mf/deref shape-ref)
state (mf/deref refs/colorpicker)
gradient (:gradient state)
stops (:stops state)
cap-stops? (or (features/use-feature "render-wasm/v1") (contains? cfg/flags :binary-fills))
stops (if cap-stops?
(vec (take shp/MAX-GRADIENT-STOPS (:stops state)))
(:stops state))
editing-stop (:editing-stop state)]
(when (and (some? gradient) (= id (:shape-id gradient)))

View file

@ -1,16 +1,13 @@
(ns app.render-wasm.serializers.fills
(:require
[app.common.types.shape :as shp]
[app.common.uuid :as uuid]
[app.render-wasm.serializers.color :as clr]))
(def ^:private GRADIENT-STOP-SIZE 8)
(def ^:private GRADIENT-BASE-SIZE 28)
;; TODO: Define in shape model
(def ^:private MAX-GRADIENT-STOPS 16)
(def GRADIENT-BYTE-SIZE
(+ GRADIENT-BASE-SIZE (* MAX-GRADIENT-STOPS GRADIENT-STOP-SIZE)))
(def GRADIENT-BYTE-SIZE 156)
(def SOLID-BYTE-SIZE 4)
(def IMAGE-BYTE-SIZE 28)
@ -48,7 +45,7 @@
end-x (:end-x gradient)
end-y (:end-y gradient)
width (or (:width gradient) 0)
stops (take MAX-GRADIENT-STOPS (:stops gradient))
stops (take shp/MAX-GRADIENT-STOPS (:stops gradient))
type (if (= (:type gradient) :linear) 0x01 0x02)]
(.setUint8 dview offset type true)
(.setFloat32 dview (+ offset 4) start-x true)