汇聚与 sibling aggregator¶
一棵树必须描述两张图。 本页讲的是第二张图 —— 以及那条决定每个 composite 收集什么的唯一规则。
为什么有两套层级¶
Harmony 的 Node View 同时承载两张连接图:
| 层级 | 颜色 | 连接 | 含义 |
|---|---|---|---|
| Input / Transform | 🟢 绿 | peg → 它的子级 | "这个带动那个" |
| Output / Composite | 🔵 蓝 | comp ← 输入它的东西 | "这个合成那个" |
它们不是同一棵树。一个 peg 可以驱动多个互不相同的合成,而一个 composite 可以汇集任何单个 peg 都不带动的东西。
staging 树就是一棵树 —— 它只有一种父子关系。所以 OSA 把它用在绿色图上,那里缩进本身就已经是你想要的意思:子行就是子节点。
这样蓝色图就没有缩进可用了。它的答案是在兄弟之间的位置。
sibling aggregator¶
没有绿色容器的 composite,汇聚它下方的兄弟。
这就是 sibling aggregator(兄弟汇聚器)。它自己没有可以往里看的作用域,所以它顺着列表往下看。
Comp-1 ← starts block 1
Peg-1 ← feeds Comp-1 … but see below: what enters is Comp-2's output
Comp-2 ← starts an inner block; its output IS Peg-1's output
Drawing-1 ← feeds Comp-2
Drawing-2 ← feeds Comp-2
Drawing-3 ← feeds Comp-1
Comp-3 ← ENDS block 1, starts block 2
Drawing-4 ← feeds Comp-3
Drawing-5 ← feeds Comp-3
念出来就是:一个 comp 开启一个区块,它之后的一切都流进它,而同一层级上的下一个 comp 关闭这个区块并开启新的一个。
peg 不喂给 comp —— 喂过去的是它那套层级的输出
Peg-1 是一个变换,不是一张图像。到达 Comp-1 的是 Peg-1 之下所有东西的结果,而那个结果是从 Comp-2 出来的 —— Peg-1 作用域内的那个 comp,它已经汇集了 Drawing-1 和 Drawing-2。
所以连线是 Comp-2 → Comp-1。Peg-1 在绿色图上;它从不出现在蓝色图上。
把 Comp-2 拿掉,Peg-1 就没有 comp 替它说话 —— 这时 Drawing-1 和 Drawing-2 会冒泡上来,各自单独接进 Comp-1,变成两个输入而不是一个。这就是有 comp 的 peg 和没有 comp 的 peg 之间的全部差别,详见容器输出什么。
而 Peg-1 里的第二个 comp 意味着有两个输出到达 Comp-1 —— 每个区块一个:
Comp-1
Peg-1 ← now hands Comp-1 TWO outputs
Comp-2 ← block A → its output goes to Comp-1
Drawing-1 → feeds Comp-2
Comp-2b ← block B: closes A, opens its own
Drawing-2 → feeds Comp-2b
← Comp-2b's output ALSO goes to Comp-1
每个在容器内结束的区块都会作为它自己的输出冒泡上来,按树的顺序。所以从 Peg-1 到达 Comp-1 的连线数量,正好等于 Peg-1 关闭的区块数量 —— 一个 comp,一根线;两个 comp,两根线;完全没有 comp,则每个 drawing 一根线。
什么会打断这条链¶
同一层级上的另一个 composite。 只有它会。
当遍历在同一批兄弟中遇到第二个 comp 时,它会冲刷当前区块并开启一个全新的。没有标记要设,也没有选项要勾:第二个 comp 的存在本身就是边界。
这就是为什么你可以把一个场景铺成一列扁平的兄弟,仍然从中得到几套彼此分开的合成 —— 你只需在每组该结束的地方放一个 comp 来分隔它们。
决定收子级还是收兄弟的规则¶
只有绿色容器才创建作用域,而绿色容器正好只有两种:Peg 和 Group。
| 槽位 | comp 汇聚 | 为什么 |
|---|---|---|
单独的 C |
下方的兄弟 | 除了一个汇聚点什么都不是 |
D-C |
下方的兄弟 | 没有绿色容器,所以没有作用域 |
BD-C |
下方的兄弟 | backdrop 是视觉的 —— 它不拥有节点 |
BD-D-C |
下方的兄弟 | 同上:backdrop 对接线毫无影响 |
P-C、G-C |
子级 | 绿色容器创建了一个内部作用域 |
P-D-C、G-D-C |
子级 | 同上 |
BD-P-C、BD-P-G-D-C |
子级 | 起作用的是 peg;backdrop 只是画了个框 |
backdrop 从不改变谁来汇聚
这一点大家最容易搞错,值得直说:BD 是视觉的。BG=BD-C 汇聚兄弟的方式和裸的 C 完全一样,因为 backdrop 和 comp 之间没有绿色连接,comp 无从借它收集子级。只有 P 或 G 才能把一个 comp 变成子级汇聚器。
带 Peg(或 Group)+ Comp 的 super node¶
现在来看你每天都在搭的那种情况。
一个既有绿色容器又有 comp 的槽位 —— hero=P-C、hero=BD-P-G-C —— 会让这个 comp 按树的顺序汇聚它的直接子级……
……直到第一个本身就是 comp 的子级为止。 那个子级开启了一个新的 sibling-aggregator 区块,它之后的一切归那个区块。
hero=P-C ← green scope; its comp collects the children below
├── arm=D → feeds hero-C
├── leg=D → feeds hero-C
├── fx=C ← a comp among the children: block boundary
├── glow=D → feeds fx-C
└── dust=D → feeds fx-C
hero-C 拿到 arm 和 leg。fx-C 拿到 glow 和 dust,而继续往上走的是 fx-C 自己的输出。
这就是整套合成语言:绿色容器开启一个作用域,里面的 comp 一直收集,直到下一个 comp 叫停。
容器输出什么¶
| 这个容器 | 它的输出 |
|---|---|
| 有内部 comp | 单个输出 —— 那个 comp,或它之后的最后一个效果 |
| 没有内部 comp | 多输出:里面的每个 drawing 都冒泡上来 |
没有 comp 的 peg 不合并任何东西。它交给父级一个列表,每一项都单独接进父级的 comp。
SuperNode=P-D-C
├── Peg-1 (P only, no Comp)
│ ├── Drawing-A
│ └── Drawing-B
└── Drawing-C
Peg-1 返回 [Drawing-A, Drawing-B],所以 SuperNode-C 收到四个输入:
| 端口 | 输入 |
|---|---|
| 3 | SuperNode 自己的 drawing |
| 2 | Drawing-A(从 Peg-1 冒泡上来) |
| 1 | Drawing-B(从 Peg-1 冒泡上来) |
| 0 | Drawing-C |
在 peg 里放一个 comp,这两个就会塌缩成一个输入。这就是 Peg-1=P 和 Peg-1=P-C 的实际差别,也通常就是一张图上连线比你预期多的原因。
端口顺序¶
蓝色图有方向,而且是固定的:
| 端口 | 渲染在 | 位于 |
|---|---|---|
| 端口 N(最高) | 前面 | Node View 的左侧 |
| 端口 0(最低) | 后面 | Node View 的右侧 |
所以 Node View 里最左边的元素在渲染的最上层,最右边的在最后面。在 super node 内部,槽位自己的 drawing 占据最前的端口,子级占据它下面的端口,从左到右。
这也是为什么 Wide Bus 把你树的顶端放在左边:树的顺序、端口的顺序和渲染的顺序是同一个顺序,读法也一样。
在树里读出它¶
你不必在脑子里模拟这一切 —— Details 列已经告诉你每一行会搭出什么,而上面那条规则告诉你它的 comp 会收集什么:
BACKGROUND (Backdrop > Peg > Comp) ← green scope: collects its children
sky (Read) → feeds BACKGROUND-C
clouds (Read) → feeds BACKGROUND-C
EFFECTS (Backdrop > Peg > Comp > Effects)
如果某个 comp 收集的东西不对,只有两个问题要问:那个槽位上有绿色容器吗? 以及在同一批兄弟中,它上面还有另一个 comp 吗?