From 9cb53adb44265962c0f32465e6f3f51c64da5d01 Mon Sep 17 00:00:00 2001 From: Miguel de Benito Delgado Date: Fri, 2 Oct 2026 10:41:13 +0200 Subject: [PATCH] :construction: WIP Step 17 Support path construction --- .../benches/render_wasm/scenes/builder.clj | 56 +++++++++++---- .../benches/render_wasm/scenes/builder.cljs | 15 ++++ .../benches/render_wasm/scenes/paths.cljs | 72 +++++++++++++++++++ 3 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 frontend/test/benches/render_wasm/scenes/paths.cljs diff --git a/frontend/test/benches/render_wasm/scenes/builder.clj b/frontend/test/benches/render_wasm/scenes/builder.clj index 3f68982cd8..d62e56bf62 100644 --- a/frontend/test/benches/render_wasm/scenes/builder.clj +++ b/frontend/test/benches/render_wasm/scenes/builder.clj @@ -50,10 +50,10 @@ "Adds one rectangle to the current scene scope and returns its uuid. Forms: `(rect)`, `(rect attrs)`, `(rect label)`, `(rect label attrs)`. - A one-argument call is resolved at runtime: a map is `attrs`, anything - else is a label. `label` must be a keyword or vector, stored in the - instance `:refs`; duplicates and other label types are rejected. `attrs` - override the generated defaults attribute by attribute." + A one-argument call is resolved at runtime: a map is interpreted as attributes + and anything else is a label. `label` must be a keyword or vector, stored in + the instance `:refs`; duplicates and other label types are rejected. `attrs` + override the defaults attribute by attribute." ([] `(benches.render-wasm.scenes.builder/rect! benches.render-wasm.scenes.builder/*state* @@ -75,14 +75,42 @@ ~label ~attrs))) +(defmacro path + "Adds one path to the current scene scope. + + Forms: `(path)`, `(path attrs)`, `(path label)`, `(path label attrs)`. + A one-argument call is resolved at runtime: a map is interpreted as attributes + and anything else is a label. `label` must be a keyword or vector, stored in + the instance `:refs`; duplicates and other label types are rejected. `attrs` + override the defaults attribute by attribute. + " + ([] + `(benches.render-wasm.scenes.builder/path! + benches.render-wasm.scenes.builder/*state* + benches.render-wasm.scenes.builder/*defaults* + nil + {})) + ([label-or-attrs] + `(benches.render-wasm.scenes.builder/path! + benches.render-wasm.scenes.builder/*state* + benches.render-wasm.scenes.builder/*defaults* + ~label-or-attrs + {})) + ([label attrs] + `(benches.render-wasm.scenes.builder/path! + benches.render-wasm.scenes.builder/*state* + benches.render-wasm.scenes.builder/*defaults* + ~label + ~attrs))) + (defmacro circle "Adds one ellipse to the current scene scope and returns its uuid. Forms: `(circle)`, `(circle attrs)`, `(circle label)`, `(circle label attrs)`. - A one-argument call is resolved at runtime: a map is `attrs`, anything - else is a label. `label` must be a keyword or vector, stored in the - instance `:refs`; duplicates and other label types are rejected. `attrs` - override the generated defaults attribute by attribute." + A one-argument call is resolved at runtime: a map is interpreted as attributes + and anything else is a label. `label` must be a keyword or vector, stored in + the instance `:refs`; duplicates and other label types are rejected. `attrs` + override the defaults attribute by attribute." ([] `(benches.render-wasm.scenes.builder/circle! benches.render-wasm.scenes.builder/*state* @@ -154,11 +182,13 @@ Forms: `(bool attrs & body)` and `(bool label attrs & body)`. `label` is a literal keyword or vector stored in the instance `:refs`. `attrs` must carry `:bool-type` (`:union`, `:difference`, `:intersection` or - `:exclude`). Content and geometry derive from the children after they - finalize, so geometry, content and transform attrs are rejected. Fills, - strokes, shadows and blurs are inherited from the head child (first for - `:difference`, last otherwise) unless attrs supply them. A bool needs at - least one child and cannot contain frames." + `:exclude`). + + Content and geometry derive from the children after they finalize, so geometry, + content and transform attrs are rejected. Fills, strokes, shadows and blurs + are inherited from the head child (first for `:difference`, last otherwise) + unless attrs supply them. A bool needs at least one child and cannot contain + frames." [label-or-attrs & body] (if (or (keyword? label-or-attrs) (vector? label-or-attrs)) `(benches.render-wasm.scenes.builder/with-container diff --git a/frontend/test/benches/render_wasm/scenes/builder.cljs b/frontend/test/benches/render_wasm/scenes/builder.cljs index 080f20ff39..02e674e61a 100644 --- a/frontend/test/benches/render_wasm/scenes/builder.cljs +++ b/frontend/test/benches/render_wasm/scenes/builder.cljs @@ -258,6 +258,21 @@ (add-object! state label shape) id)) +(defn path! + "Adds one path to the scope and returns its uuid. + Runtime primitive behind the `path` macro." + [state defaults label attrs] + (let [state (ensure-state state) + [label attrs] (normalize-label-and-attrs label attrs) + rng (:rng @state) + attrs (resolve-attrs rng (:path defaults) attrs) + id (rng-uuid rng) + shape (cts/setup-shape (merge attrs + {:id id + :type :path}))] + (add-object! state label shape) + id)) + (defn circle! "Adds one circle to the scope and returns its uuid. Runtime primitive behind the `circle` macro." diff --git a/frontend/test/benches/render_wasm/scenes/paths.cljs b/frontend/test/benches/render_wasm/scenes/paths.cljs new file mode 100644 index 0000000000..800f07e9f5 --- /dev/null +++ b/frontend/test/benches/render_wasm/scenes/paths.cljs @@ -0,0 +1,72 @@ +;; 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 SUBSIDIARY SL + +(ns benches.render-wasm.scenes.paths + "Curved paths" + (:require + [app.common.schema :as sm] + [app.common.types.path :as path] + [benches.render-wasm.scenes.builder :as sb :include-macros true] + [benches.render-wasm.scenes.camera :as camera] + [benches.render-wasm.scenes.core :as core :include-macros true])) + +(def ^:private default-params + {:count 1000 :width 1920 :height 1080}) + +(def ^:private schema:params + [:map {:closed true} + [:count [:int {:min 1 :max 100000}]] + [:width ::sm/positive-safe-number] + [:height ::sm/positive-safe-number]]) + +(defn- curved-content + [{:keys [width height]}] + (fn [rng] + (let [x (sb/rng-int rng 0 width) + y (sb/rng-int rng 0 height) + w (sb/rng-int rng 20 100) + h (sb/rng-int rng 20 100)] + (path/from-plain + [{:command :move-to :params {:x x :y y}} + {:command :curve-to + :params {:c1x (+ x w) :c1y y + :c2x (+ x w) :c2y (+ y h) + :x (+ x w) :y (+ y h)}} + {:command :line-to :params {:x x :y (+ y h)}} + {:command :close-path}])))) + +(defn build + [params] + (let [params (merge default-params params)] + (sb/scene {:seed (:seed params) + :root {:x 0 :y 0 :width (:width params) :height (:height params)} + :defaults {:path {:content (curved-content params) + :fills (sb/gen-vector (sb/gen-fill)) + :strokes (sb/gen-vector (sb/gen-stroke 3))}}} + (doseq [_ (range (:count params))] + (sb/path))))) + +(core/defscene :paths + {:version 1 + :description "Curved paths" + :params-schema schema:params} + build) + +(def ^:private base-view {:scale 1 :x 0 :y 0}) + +(core/defcase :paths/load :paths + {:params default-params :view base-view :context :fresh} + (fn [rtx] (camera/load! rtx))) + +(core/defcase :paths/pan :paths + {:params default-params :view base-view :context :reuse + :operation {:steps 20 :dx 160 :dy 40 :settle-ms 100}} + (fn [rtx] (camera/pan! rtx))) + +(core/defcase :paths/zoom :paths + {:params default-params :view base-view :context :reuse + :operation {:steps 20 :factor 1.5 :settle-ms 100}} + (fn [rtx] (camera/zoom! rtx)))