🚧 WIP Step 17 Support path construction

This commit is contained in:
Miguel de Benito Delgado 2026-10-02 10:41:13 +02:00
parent 5ca9e06fa7
commit 9cb53adb44
3 changed files with 130 additions and 13 deletions

View File

@ -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

View File

@ -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."

View File

@ -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)))