29 Membranes
The fourth stage of Plotje’s pipeline turns a fully-resolved plan into a PlotjeMembrane: a tree of layout-and-drawing primitives sized exactly for the output canvas, ready to be rendered to SVG, PNG, or any other format the Membrane library supports.
A PlotjeMembrane is itself a Membrane UI component. It implements the standard Membrane protocols (IOrigin, IBounds, IChildren), so any tool that consumes Membrane components consumes a Plotje plot without special handling, and any Plotje plot can be composed into a larger Membrane interface alongside hand-built Membrane elements. This chapter walks the membrane stage end to end and shows that interop in action.
(ns plotje-book.membranes
(:require
;; Kindly notebook annotations
[scicloj.kindly.v4.kind :as kind]
;; Plotje, the public API
[scicloj.plotje.api :as pj]
;; Rdatasets for example data
[scicloj.metamorph.ml.rdatasets :as rdatasets]
;; Membrane core: protocols and primitives
[membrane.ui :as ui]))A running example
Throughout the chapter we lift one iris scatter into the membrane stage. The pose looks like:
(def iris-pose
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width
{:color :species})
(pj/options {:title "Iris" :y-label "width"})))iris-poseProducing a membrane
The composition shortcut pj/membrane runs the full pipeline up through the membrane stage in one call:
(def iris-membrane (pj/membrane iris-pose))(pj/membrane? iris-membrane)trueThe step pj/plan->membrane does the same starting from an already-resolved plan – handy when you have a plan in hand from inspection and want to skip re-running the earlier stages:
(def iris-plan (pj/plan iris-pose))(pj/membrane? (pj/plan->membrane iris-plan))trueBoth routes return the same shape: a PlotjeMembrane record.
Options flow through the explicit route
pj/membrane is the shortcut. The same membrane also comes out of the explicit two-step route – pj/pose->draft followed by pj/draft->membrane. Options set on the pose with pj/options, including the title, the axis labels, and the theme, ride on the draft’s options and reach the membrane stage on either route.
Here is a themed pose, rendered so the theme is visible:
(def themed-pose
(-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width
{:color :species})
(pj/options {:title "Iris"
:theme {:bg "aliceblue" :grid "#cccccc"}})))themed-poseTaking that pose through the explicit route gives the same membrane as the shortcut – the theme is carried through, not dropped:
(= (pj/membrane themed-pose)
(-> themed-pose
pj/pose->draft
pj/draft->membrane))trueAnatomy
The record carries three structural fields plus optional :plotje/-namespaced attributes. The full value prints with every drawable in :drawables, so it is long, but every part is visible:
iris-membrane{:drawables
[{:x 270.25,
:y 14,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "Iris",
:font {:name nil, :size 15, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 12,
:y 197.5,
:drawable
{:degrees -90,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "width",
:font
{:name nil, :size 13, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}}
{:x 270.25,
:y 382.0,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "sepal length",
:font {:name nil, :size 13, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 508.0,
:y 35,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "species",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil}})}}
{:x 508.0,
:y 53,
:drawable
[{:x 0,
:y 0,
:drawable
{:color
[0.8941176470588236 0.10196078431372549 0.10980392156862745 1.0],
:drawables
({:style :membrane.ui/style-fill,
:drawables [{:width 8, :height 8, :border-radius 4.0}]})}}
{:x 12,
:y 0,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "setosa",
:font
{:name nil,
:size 10,
:weight nil,
:width nil,
:slant nil}})}}]}
{:x 508.0,
:y 69,
:drawable
[{:x 0,
:y 0,
:drawable
{:color
[0.21568627450980393 0.49411764705882355 0.7215686274509804 1.0],
:drawables
({:style :membrane.ui/style-fill,
:drawables [{:width 8, :height 8, :border-radius 4.0}]})}}
{:x 12,
:y 0,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "versicolor",
:font
{:name nil,
:size 10,
:weight nil,
:width nil,
:slant nil}})}}]}
{:x 508.0,
:y 85,
:drawable
[{:x 0,
:y 0,
:drawable
{:color
[0.30196078431372547 0.6862745098039216 0.2901960784313726 1.0],
:drawables
({:style :membrane.ui/style-fill,
:drawables [{:width 8, :height 8, :border-radius 4.0}]})}}
{:x 12,
:y 0,
:drawable
{:color [0.2 0.2 0.2 1.0],
:drawables
({:text "virginica",
:font
{:name nil,
:size 10,
:weight nil,
:width nil,
:slant nil}})}}]}
{:x 52.5,
:y 43.0,
:drawable
{:color
[0.9098039215686274 0.9098039215686274 0.9098039215686274 1.0],
:drawables
({:style :membrane.ui/style-fill,
:drawables [{:width 435.5, :height 309.0}]})}}
{:x 42.5,
:y 33.0,
:drawable
[{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([51.790404040404034 10] [51.790404040404034 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([106.77777777777777 10] [106.77777777777777 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([161.7651515151515 10] [161.7651515151515 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([216.75252525252526 10] [216.75252525252526 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([271.73989898989896 10] [271.73989898989896 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([326.72727272727275 10] [326.72727272727275 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([381.7146464646464 10] [381.7146464646464 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([436.7020202020202 10] [436.7020202020202 319.0])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 304.95454545454544] [445.5 304.95454545454544])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 246.4318181818182] [445.5 246.4318181818182])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 187.90909090909093] [445.5 187.90909090909093])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 129.38636363636365] [445.5 129.38636363636365])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 70.8636363636364] [445.5 70.8636363636364])}]}]})}
{:color
[0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0],
:drawables
({:stroke-width 0.6,
:drawables
[{:style :membrane.ui/style-stroke,
:drawables
[{:points
([10 12.340909090909179] [445.5 12.340909090909179])}]}]})}
{:offset [10 10],
:bounds [435.5 309.0],
:drawable
[{:x 114.77525252525247,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 0}
{:x 92.78030303030306,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 1}
{:x 70.78535353535355,
:y 161.5,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 2}
{:x 59.78787878787873,
:y 173.20454545454547,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 3}
{:x 103.77777777777777,
:y 114.68181818181819,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 4}
{:x 147.76767676767682,
:y 79.56818181818187,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 5}
{:x 59.78787878787873,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 6}
{:x 103.77777777777777,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 7}
{:x 37.79292929292932,
:y 196.61363636363637,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 8}
{:x 92.78030303030306,
:y 173.20454545454547,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 9}
{:x 147.76767676767682,
:y 102.97727272727275,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 10}
{:x 81.78282828282826,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 11}
{:x 81.78282828282826,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 12}
{:x 26.795454545454515,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 13}
{:x 191.75757575757575,
:y 67.8636363636364,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 14}
{:x 180.760101010101,
:y 21.04545454545456,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 15}
{:x 147.76767676767682,
:y 79.56818181818187,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 16}
{:x 114.77525252525247,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 17}
{:x 180.760101010101,
:y 91.27272727272734,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 18}
{:x 114.77525252525247,
:y 91.27272727272734,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 19}
{:x 147.76767676767682,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 20}
{:x 114.77525252525247,
:y 102.97727272727275,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 21}
{:x 59.78787878787873,
:y 114.68181818181819,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 22}
{:x 114.77525252525247,
:y 149.7954545454546,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 23}
{:x 81.78282828282826,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 24}
{:x 103.77777777777777,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 25}
{:x 103.77777777777777,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 26}
{:x 125.77272727272731,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 27}
{:x 125.77272727272731,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 28}
{:x 70.78535353535355,
:y 161.5,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 29}
{:x 81.78282828282826,
:y 173.20454545454547,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 30}
{:x 147.76767676767682,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 31}
{:x 125.77272727272731,
:y 56.15909090909099,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 32}
{:x 158.7651515151515,
:y 44.454545454545496,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 33}
{:x 92.78030303030306,
:y 173.20454545454547,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 34}
{:x 103.77777777777777,
:y 161.5,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 35}
{:x 158.7651515151515,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 36}
{:x 92.78030303030306,
:y 114.68181818181819,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 37}
{:x 37.79292929292932,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 38}
{:x 114.77525252525247,
:y 138.09090909090912,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 39}
{:x 103.77777777777777,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 40}
{:x 48.790404040404034,
:y 266.8409090909091,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 41}
{:x 37.79292929292932,
:y 161.5,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 42}
{:x 103.77777777777777,
:y 126.38636363636365,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 43}
{:x 114.77525252525247,
:y 91.27272727272734,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 44}
{:x 81.78282828282826,
:y 184.90909090909093,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 45}
{:x 114.77525252525247,
:y 91.27272727272734,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 46}
{:x 59.78787878787873,
:y 161.5,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 47}
{:x 136.770202020202,
:y 102.97727272727275,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 48}
{:x 103.77777777777777,
:y 149.7954545454546,
:drawable
[{:color
[0.8941176470588236
0.10196078431372549
0.10980392156862745
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 49}
{:x 323.72727272727275,
:y 161.5,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 50}
{:x 257.74242424242425,
:y 161.5,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 51}
{:x 312.72979797979804,
:y 173.20454545454547,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 52}
{:x 158.7651515151515,
:y 266.8409090909091,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 53}
{:x 268.73989898989896,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 54}
{:x 180.760101010101,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 55}
{:x 246.74494949494945,
:y 149.7954545454546,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 56}
{:x 92.78030303030306,
:y 255.13636363636363,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 57}
{:x 279.73737373737373,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 58}
{:x 125.77272727272731,
:y 220.02272727272725,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 59}
{:x 103.77777777777777,
:y 301.95454545454544,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 60}
{:x 202.75505050505052,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 61}
{:x 213.75252525252526,
:y 278.5454545454545,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 62}
{:x 224.74999999999994,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 63}
{:x 169.7626262626262,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 64}
{:x 290.7348484848485,
:y 173.20454545454547,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 65}
{:x 169.7626262626262,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 66}
{:x 191.75757575757575,
:y 220.02272727272725,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 67}
{:x 235.74747474747477,
:y 278.5454545454545,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 68}
{:x 169.7626262626262,
:y 243.4318181818182,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 69}
{:x 202.75505050505052,
:y 161.5,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 70}
{:x 224.74999999999994,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 71}
{:x 246.74494949494945,
:y 243.4318181818182,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 72}
{:x 224.74999999999994,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 73}
{:x 257.74242424242425,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 74}
{:x 279.73737373737373,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 75}
{:x 301.7323232323232,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 76}
{:x 290.7348484848485,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 77}
{:x 213.75252525252526,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 78}
{:x 180.760101010101,
:y 231.72727272727272,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 79}
{:x 158.7651515151515,
:y 255.13636363636363,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 80}
{:x 158.7651515151515,
:y 255.13636363636363,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 81}
{:x 191.75757575757575,
:y 220.02272727272725,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 82}
{:x 213.75252525252526,
:y 220.02272727272725,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 83}
{:x 147.76767676767682,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 84}
{:x 213.75252525252526,
:y 138.09090909090912,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 85}
{:x 290.7348484848485,
:y 173.20454545454547,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 86}
{:x 246.74494949494945,
:y 266.8409090909091,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 87}
{:x 169.7626262626262,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 88}
{:x 158.7651515151515,
:y 243.4318181818182,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 89}
{:x 158.7651515151515,
:y 231.72727272727272,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 90}
{:x 224.74999999999994,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 91}
{:x 191.75757575757575,
:y 231.72727272727272,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 92}
{:x 103.77777777777777,
:y 266.8409090909091,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 93}
{:x 169.7626262626262,
:y 220.02272727272725,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 94}
{:x 180.760101010101,
:y 184.90909090909093,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 95}
{:x 180.760101010101,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 96}
{:x 235.74747474747477,
:y 196.61363636363637,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 97}
{:x 114.77525252525247,
:y 243.4318181818182,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 98}
{:x 180.760101010101,
:y 208.31818181818184,
:drawable
[{:color
[0.21568627450980393
0.49411764705882355
0.7215686274509804
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 99}
{:x 246.74494949494945,
:y 149.7954545454546,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 100}
{:x 191.75757575757575,
:y 220.02272727272725,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 101}
{:x 334.7247474747474,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 102}
{:x 246.74494949494945,
:y 196.61363636363637,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 103}
{:x 268.73989898989896,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 104}
{:x 389.7121212121212,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 105}
{:x 92.78030303030306,
:y 243.4318181818182,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 106}
{:x 356.71969696969694,
:y 196.61363636363637,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 107}
{:x 290.7348484848485,
:y 243.4318181818182,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 108}
{:x 345.72222222222223,
:y 114.68181818181819,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 109}
{:x 268.73989898989896,
:y 161.5,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 110}
{:x 257.74242424242425,
:y 220.02272727272725,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 111}
{:x 301.7323232323232,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 112}
{:x 180.760101010101,
:y 243.4318181818182,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 113}
{:x 191.75757575757575,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 114}
{:x 257.74242424242425,
:y 161.5,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 115}
{:x 268.73989898989896,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 116}
{:x 400.70959595959596,
:y 91.27272727272734,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 117}
{:x 400.70959595959596,
:y 231.72727272727272,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 118}
{:x 213.75252525252526,
:y 278.5454545454545,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 119}
{:x 312.72979797979804,
:y 161.5,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 120}
{:x 169.7626262626262,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 121}
{:x 400.70959595959596,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 122}
{:x 246.74494949494945,
:y 220.02272727272725,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 123}
{:x 290.7348484848485,
:y 149.7954545454546,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 124}
{:x 345.72222222222223,
:y 161.5,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 125}
{:x 235.74747474747477,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 126}
{:x 224.74999999999994,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 127}
{:x 257.74242424242425,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 128}
{:x 345.72222222222223,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 129}
{:x 367.71717171717177,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 130}
{:x 422.7045454545455,
:y 91.27272727272734,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 131}
{:x 257.74242424242425,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 132}
{:x 246.74494949494945,
:y 208.31818181818184,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 133}
{:x 224.74999999999994,
:y 231.72727272727272,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 134}
{:x 400.70959595959596,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 135}
{:x 246.74494949494945,
:y 138.09090909090912,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 136}
{:x 257.74242424242425,
:y 173.20454545454547,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 137}
{:x 213.75252525252526,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 138}
{:x 312.72979797979804,
:y 173.20454545454547,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 139}
{:x 290.7348484848485,
:y 173.20454545454547,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 140}
{:x 312.72979797979804,
:y 173.20454545454547,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 141}
{:x 191.75757575757575,
:y 220.02272727272725,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 142}
{:x 301.7323232323232,
:y 161.5,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 143}
{:x 290.7348484848485,
:y 149.7954545454546,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 144}
{:x 290.7348484848485,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 145}
{:x 246.74494949494945,
:y 243.4318181818182,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 146}
{:x 268.73989898989896,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 147}
{:x 235.74747474747477,
:y 138.09090909090912,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 148}
{:x 202.75505050505052,
:y 184.90909090909093,
:drawable
[{:color
[0.30196078431372547
0.6862745098039216
0.2901960784313726
0.75],
:drawables
({:style :membrane.ui/style-fill,
:drawables
[{:width 6.0, :height 6.0, :border-radius 3.0}]})}
nil],
:row-idx 149}]}
{:x 51.790404040404034,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "4.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 106.77777777777777,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "5.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 161.7651515151515,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "5.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 216.75252525252526,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "6.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 271.73989898989896,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "6.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 326.72727272727275,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "7.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 381.7146464646464,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "7.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 436.7020202020202,
:y 331.0,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "8.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "middle"})}}
{:x 7.0,
:y 299.45454545454544,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "2.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}
{:x 7.0,
:y 240.9318181818182,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "2.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}
{:x 7.0,
:y 182.40909090909093,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "3.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}
{:x 7.0,
:y 123.88636363636365,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "3.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}
{:x 7.0,
:y 65.3636363636364,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "4.0",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}
{:x 7.0,
:y 6.840909090909179,
:drawable
{:color [0.4 0.4 0.4 1.0],
:drawables
({:text "4.5",
:font
{:name nil, :size 11, :weight nil, :width nil, :slant nil},
:text-anchor "end"})}}]}],
:width 600,
:height 400,
:plotje/title "Iris"}A summary view of the structural pieces:
{:width (ui/width iris-membrane)
:height (ui/height iris-membrane)
:origin (ui/origin iris-membrane)
:title (:plotje/title iris-membrane)
:n-drawables (count (ui/children iris-membrane))}{:width 600, :height 400, :origin [0 0], :title "Iris", :n-drawables 9}The width and height come from the plan – the membrane stage does not recompute them; it inherits the canvas size the plan resolved. The origin is [0 0] because a PlotjeMembrane is the full canvas; when it is embedded in a larger Membrane layout, the layout’s translation places it. The top-level drawables for this iris scatter are the title, the axis labels, the panel (its background and grid, and the marks drawn on it), and the legend (a heading plus one row per category).
The map keys, as the record presents them, are three record fields plus the namespaced title:
(sort (filter keyword? (keys iris-membrane)))(:drawables :height :width :plotje/title)The Membrane protocols
A PlotjeMembrane implements three Membrane UI protocols:
IOrigin– the component’s top-left corner inside its parent. Plotje’s membrane is the canvas itself, so(membrane.ui/origin m)returns[0 0].IBounds– the component’s[width height]. Read directly via(membrane.ui/width m)and(membrane.ui/height m), both of which derive from(membrane.ui/bounds m).IChildren– the sub-elements for traversal. Returns the underlying:drawablesvector.
Why these three and not more? Membrane’s protocols partition into “what every UI element supports” (origin, bounds, children) and “what the element does” (drawing, mouse events, key events, …). Plotje implements the first set so a PlotjeMembrane participates in every Membrane consumer that walks a tree generically. Drawing is delegated to the children: when a Membrane backend draws our record, the default IDraw impl walks (children record) and draws each child individually – the existing Translate, WithColor, Path, Label primitives already have per-backend draw implementations.
Title and namespaced attributes
The plot title rides as :plotje/title – not a defrecord field but a namespaced map entry assoc’d onto the record. This keeps the record’s arity stable as we add per-membrane attributes in the future. A pose without a title produces a membrane without that key:
(:plotje/title (pj/membrane (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width))))nilFuture per-membrane attributes will use the same :plotje/* namespace (for instance :plotje/subtitle or :plotje/caption). A backend reading the membrane can pick up whichever ones it recognizes and ignore the rest – the :plotje/ prefix makes our keys collision-free with any other library composing into the same map.
Composing with other Membrane components
Because a PlotjeMembrane is a Membrane UI component, you can drop it into any Membrane layout. Two Plotje plots side by side is one line:
(def two-up
(ui/horizontal-layout
(pj/membrane (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :sepal-width
{:color :species})
(pj/options {:title "Sepal length vs sepal width"
:y-label "width"})))
(pj/membrane (-> (rdatasets/datasets-iris)
(pj/lay-point :sepal-length :petal-length
{:color :species})
(pj/options {:title "Sepal length vs petal length"
:y-label "petal"})))))The result is itself a Membrane component with bounds derived from its children. Two 600 by 400 plots side by side, with a 1-unit gap that horizontal-layout inserts, give a 1201 by 400 canvas:
{:width (ui/width two-up)
:height (ui/height two-up)}{:width 1201, :height 400}The combined component is no longer a PlotjeMembrane (it is whatever horizontal-layout chose to return – here, a vector of the two children), but it speaks the same protocol vocabulary, so any Membrane backend can render it. To produce a raster image we bypass pj/membrane->plot (which validates a PlotjeMembrane specifically) and call Membrane’s Java2D backend directly:
(def two-up-png
((requiring-resolve 'membrane.java2d/draw-to-image)
two-up
[(ui/width two-up) (ui/height two-up)]))(instance? java.awt.image.BufferedImage two-up-png)trueThe same pose stages, rendered as raster:
two-up-png
The membrane stage is a separate boundary for this reason: Plotje produces values that any Membrane consumer understands, and Plotje plots compose with hand-built Membrane elements. Nothing about the value type is Plotje-specific beyond the namespaced title key.
Rendering through Plotje’s backends
The composition shortcut pj/plot is the convenience case; the explicit step pj/membrane->plot dispatches on a format keyword. :svg is always available – the path that auto-rendered iris-pose at the top of the chapter:
(pj/membrane->plot iris-membrane :svg {}):bufimg is registered when scicloj.plotje.render.bufimg is loaded; it produces a Java BufferedImage, the raster form used in the previous section’s composition:
(pj/membrane->plot iris-membrane :bufimg {})
The membrane stage is format-agnostic: the same iris-membrane produced one valid value, and that value renders to multiple output formats without re-walking the plan. Adding a new format means writing a defmethod on scicloj.plotje.impl.render/membrane->plot. The Extensibility chapter walks a worked example.
Clipping marks to the panel
Clipping – keeping each panel’s marks inside the panel (the Glossary entry for clip defines the term) – needs no special machinery in the membrane stage: the clip is another node in the tree. The panel’s marks are wrapped in a membrane.ui/scissor-view, a Membrane primitive that masks its contents to a rectangle, so the clip can be found and inspected like any other drawable.
A panel uses two clip rectangles. Data marks clip to the drawing area (the grey panel background); rug ticks, which sit in the axis margin on purpose, clip to the wider panel box. Which rectangle a mark uses is itself an extension point – the Extensibility chapter covers it.
Build a membrane with one mark of each kind: a line running far past a tightened y-domain (a data mark) and a rug (a margin mark).
(def clipped-membrane
(-> {:x [1 2 3 4 5] :y [10 20 15 25 18]}
(pj/lay-point :x :y)
(pj/lay-line {:data {:x [1 5] :y [-200 300]}})
(pj/lay-rug :x)
(pj/scale :y {:type :linear :domain [0 30]})
pj/membrane))The scissors sit inside the panel’s drawable, so finding them means walking the tree. Each one’s clip rectangle (offset and bounds, in drawing units): the data scissor at the drawing area, its offset the panel margin; the rug scissor at the panel box, its offset [0 0].
(->> (ui/children clipped-membrane)
(tree-seq coll? seq)
(filter #(instance? membrane.ui.ScissorView %))
(mapv #(select-keys % [:offset :bounds])))[{:offset [10 10], :bounds [542.0 342.0]}
{:offset [0 0], :bounds [562.0 362.0]}]Schema
Plotje validates the PlotjeMembrane shape against a Malli schema through pj/valid-membrane? and pj/explain-membrane – the membrane counterpart of pj/valid-plan? and pj/explain-plan for plans. Backend authors validating an incoming membrane (or constructing their own for testing) can use them:
(pj/valid-membrane? iris-membrane)trueA non-membrane value fails validation; pj/explain-membrane returns a Malli explanation map (nil when the value is valid):
(some? (pj/explain-membrane {:not :a-membrane}))trueSee also
Architecture – the full five-stage pipeline and where the membrane stage fits.
Extensibility – adding a new format by registering a
membrane->plotdefmethod, plus the other extension points (marks, stats, scales, coordinates).