Style-neutral people packs (Person, Location, Wardrobe, Light, Camera, Direction, Render), four generated example workflows (One image, Batches and grids, Pick and refine, Sets and stories) laid out left to right with wires on reroutes, and Start here notes with bookmarks and the toolbar help picture.
396 lines
22 KiB
Python
396 lines
22 KiB
Python
"""Write the example workflows in `example_workflows/` from the packs here.
|
|
|
|
python scripts/example_workflows/build.py
|
|
|
|
The output is committed; `tests/test_example_workflows.py` runs every file on
|
|
an empty library.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
|
import photo # noqa: E402
|
|
import stories # noqa: E402
|
|
from graph import MUTE, Workflow, note_value, stable_uuid, view # noqa: E402
|
|
from packs import Bundle, Module, context_value # noqa: E402
|
|
|
|
OUT = Path(__file__).resolve().parents[2] / "example_workflows"
|
|
|
|
CLEANER = json.dumps({"mode": "tags", "intensity": "balanced", "rules_override": {},
|
|
"blocklist": {"kind": "list", "entries": []}}, separators=(",", ":"))
|
|
LINKS_FOOTER = (
|
|
"\n\n---\n\n**Questions, ideas, packs to share:**\n\n"
|
|
"[Discord](https://discord.gg/BFYR9WQdVR){button pi-discord #5865f2} "
|
|
"[Community packs](https://wp.dumiflex.dev){button pi-globe #22c55e} "
|
|
"[Wiki](https://github.com/DumiFlex/ComfyUI-Wildcard-Pipeline/wiki){button pi-github #8b949e} "
|
|
"[About these examples](docs:howto-examples){button pi-th-large}"
|
|
)
|
|
MODEL_INFO = json.dumps({"version": 1, "rules": [
|
|
{"variant": "noobai", "pattern": "noob"}, {"variant": "pony", "pattern": "pony|pdxl"},
|
|
{"variant": "illustrious", "pattern": "illustrious|ilxl"},
|
|
{"variant": "animagine", "pattern": "animagine"}],
|
|
"family": "", "variant": "", "name": "",
|
|
"internal": {"family": True, "variant": True, "name": True}}, separators=(",", ":"))
|
|
|
|
|
|
HELP_TIP = (
|
|
":pi-question-circle:{#6366f1} **Help for any node:** select it and press the indigo **?** "
|
|
"in the toolbar above it. Its help page opens over the canvas, with what every socket and "
|
|
"control does. On WP Toggles, Note and Bookmark the **pencil** next to it opens the editor."
|
|
"\n\n{width=70%}"
|
|
)
|
|
JUMP_TIP = (
|
|
":pi-bookmark: Press a step's number key with the canvas focused, or click it in the "
|
|
"**Bookmarks** dock (top bar). **Start here** is a bookmark too, so the dock always brings "
|
|
"you back to this note."
|
|
)
|
|
|
|
|
|
def start_view(x: float, w: float, h: float) -> dict:
|
|
"""The Start here note's own bookmark: first in the dock, no key."""
|
|
return view(x + w / 2, h / 2, 0.7, "", 0)
|
|
|
|
|
|
def loop_config(**over: object) -> str:
|
|
cfg: dict[str, object] = {
|
|
"strategy": "hash_index", "override_seed": False, "iteration_var_name": "iteration",
|
|
"bypass": False, "iteration_internal": True, "total_internal": True, "seed_locks": {},
|
|
"bypass_frames": [],
|
|
"sweep": {"enabled": False, "limit": 64, "hold_others": True, "axes": []},
|
|
}
|
|
cfg.update(over)
|
|
return json.dumps(cfg)
|
|
|
|
|
|
def grid_config(title: str, *, label: bool, columns: int = 0) -> str:
|
|
return json.dumps({
|
|
"label": "", "position": "below" if label else "none", "align": "center",
|
|
"font": "default", "font_size": 0, "text_color": "#ffffff", "label_color": "#000000",
|
|
"grid": True, "arrange": "columns" if columns else "auto", "columns": columns,
|
|
"rows_from": "none", "row_headers": "", "columns_from": "none", "column_headers": "",
|
|
"title": title, "gap": 8, "margin": 16, "background": "#ffffff",
|
|
"header_color": "#000000", "cell_width": 512,
|
|
})
|
|
|
|
|
|
def ctx(wf: Workflow, pos: tuple[float, float], title: str, seed: int, *items: Module | Bundle,
|
|
salt: str, h: float = 560, mode: int = 0) -> dict:
|
|
return wf.add("WP_Context", pos, (600, h), title=title, mode=mode, widgets={
|
|
"seed": seed, "control_after_generate": "randomize",
|
|
"wp_modules": context_value(*items, salt=salt),
|
|
})
|
|
|
|
|
|
# ── 4 - Sets and stories ─────────────────────────────────────────────────
|
|
|
|
FILTER = json.dumps({"mode": "pause", "send_as": "same_shape", "timeout": 600,
|
|
"on_timeout": "keep_all"}, separators=(",", ":"))
|
|
ROW_H = 1760
|
|
GROUPS = [
|
|
("Every frame different", "#3f789e"),
|
|
("Photo shoot", "#a1309b"),
|
|
("Storyboard", "#b06634"),
|
|
]
|
|
# Inner groups of a mode row: (title, x, width). The toggle mutes the outer group.
|
|
PARTS = [("Lists", -20, 2500), ("Prompt", 2520, 1140), ("Image", 3680, 860),
|
|
("Pick and grid", 4560, 2220)]
|
|
# The checkpoint's wires run down one trunk each, left of the rows.
|
|
TRUNK = {"MODEL": -300, "CLIP": -312, "VAE": -324, "BOOL": -336}
|
|
|
|
|
|
def bus(wf: Workflow, src: dict, outs: dict[str, tuple[str, float]]) -> dict[str, tuple]:
|
|
"""Trunk roots for `src`'s outputs: {kind: (src, output, root reroute)}."""
|
|
return {k: (src, out, wf.route((TRUNK[k], y))) for k, (out, y) in outs.items()}
|
|
|
|
|
|
def tap(wf: Workflow, trunk: tuple, node: dict, inp: str, *points: tuple[float, float]) -> None:
|
|
"""Link a trunk into `node.inp`, along `points` (the first one on the trunk)."""
|
|
src, out, root = trunk
|
|
wf.link(src, out, node, inp, via=wf.route(*points, after=root))
|
|
|
|
|
|
def mode_row(wf: Workflow, gi: int, *, title: str, color: str, muted: int, trunks: dict,
|
|
loop_values: dict, contexts: list[tuple[str, int, list]], salt: str, prompt: str,
|
|
negative: str, label: str, cleaner: str, grid: str, text: str, prompt_note: str,
|
|
image_note: str, ks_seed: int, seeds: int | None, prefix: str,
|
|
injector: dict | None = None) -> dict[str, dict]:
|
|
"""One mode of a Sets and stories workflow, laid out left to right in four parts.
|
|
|
|
Lists (loop, Model Info, contexts) → Prompt → Image → Pick and grid. The
|
|
checkpoint comes in from the trunks along a lane under the row.
|
|
"""
|
|
y0 = gi * ROW_H
|
|
n = y0 + 380 # nodes
|
|
lane = y0 + 1516
|
|
wf.group(title, -40, y0 - 70, 6840, 1660, color)
|
|
for part, x, w in PARTS:
|
|
wf.group(part, x, y0, w, 1490, color)
|
|
|
|
def add(kind: str, pos: tuple[float, float], size: tuple[float, float], **kw) -> dict:
|
|
return wf.add(kind, pos, size, mode=muted, **kw)
|
|
|
|
loop = add("WP_ContextLoop", (0, n), (400, 460))
|
|
wf.set(loop, **loop_values)
|
|
minfo = add("WP_ModelInfo", (0, n + 500), (400, 320), widgets={"wp_model_info": MODEL_INFO})
|
|
head = [loop, minfo]
|
|
if injector:
|
|
head.append(add("WP_ContextInjector", (0, n + 880), (460, 210), **injector))
|
|
# Each head node feeds the one under it: out on the right, round, in on the left.
|
|
for k, (a, b) in enumerate(zip(head, head[1:], strict=False)):
|
|
top = b["pos"][1]
|
|
wf.link(a, "context", b, "upstream", via=wf.route(
|
|
(472 + k * 12, a["pos"][1] + 30), (472 + k * 12, top - 30), (-8, top - 30),
|
|
(-8, top + 30)))
|
|
tap(wf, trunks["MODEL"], minfo, "model", (TRUNK["MODEL"], n + 556), (-20, n + 556))
|
|
if injector and "BOOL" in trunks:
|
|
tap(wf, trunks["BOOL"], head[-1], "input_0", (TRUNK["BOOL"], n + 936), (-20, n + 936))
|
|
cs = []
|
|
for i, (ctitle, seed, items) in enumerate(contexts):
|
|
cs.append(ctx(wf, (520 + i * 660, n), ctitle, seed, *items, salt=salt, h=420,
|
|
mode=muted))
|
|
wf.link(head[-1], "context", cs[0], "upstream",
|
|
via=wf.route((500, head[-1]["pos"][1] + 30), (500, n + 30)))
|
|
for a, b in zip(cs, cs[1:], strict=False):
|
|
wf.link(a, "context", b, "upstream")
|
|
wf.add("WP_Note", (0, y0 + 50), (1000, 290), title=title, widgets={
|
|
"wp_note": note_value(text, tone="step", step=gi + 1, bookmark=view(
|
|
3370, y0 + 735, 0.24, f"{gi + 1}", gi + 1))})
|
|
|
|
# Prompt
|
|
wf.add("WP_Note", (2560, y0 + 50), (1060, 200), title=f"{title} · prompt", widgets={
|
|
"wp_note": note_value(prompt_note, tone="icon", icon="pencil", color="teal")})
|
|
asm = add("WP_PromptAssembler", (2560, n), (500, 640),
|
|
widgets={"template": prompt, "negative_template": negative})
|
|
cln = add("WP_PromptCleaner", (3120, n), (500, 520), widgets={"wp_cleaner": cleaner})
|
|
lab = add("WP_PromptAssembler", (3120, n + 580), (500, 470),
|
|
title="WP Prompt Assembler · labels",
|
|
widgets={"template": label, "negative_template": ""})
|
|
wf.link(cs[-1], "context", asm, "context")
|
|
wf.link(cs[-1], "context", lab, "context", via=wf.route(
|
|
(2500, n + 30), (2500, n + 680), (3090, n + 680), (3090, n + 610)))
|
|
wf.link(asm, "prompt", cln, "prompt")
|
|
wf.link(asm, "negative", cln, "negative")
|
|
|
|
# Image
|
|
wf.add("WP_Note", (3720, y0 + 50), (780, 200), title=f"{title} · image", widgets={
|
|
"wp_note": note_value(image_note, tone="icon", icon="image", color="neutral")})
|
|
pos = add("CLIPTextEncode", (3720, n), (400, 160), title="Positive")
|
|
neg = add("CLIPTextEncode", (3720, n + 200), (400, 160), title="Negative")
|
|
lat = add("EmptyLatentImage", (3720, n + 400), (400, 110),
|
|
widgets={"width": 832, "height": 1216, "batch_size": 1})
|
|
ks = add("KSampler", (4180, n), (320, 280), widgets={
|
|
"seed": ks_seed, "control_after_generate": "fixed", "steps": 28, "cfg": 5.5,
|
|
"sampler_name": "euler_ancestral", "scheduler": "normal", "denoise": 1})
|
|
dec = add("VAEDecode", (4180, n + 340), (320, 50))
|
|
clip_row = wf.route((TRUNK["CLIP"], lane), (3692, lane), after=trunks["CLIP"][2])
|
|
for enc, src, dy in ((pos, "prompt", 0), (neg, "negative", 200)):
|
|
wf.link(trunks["CLIP"][0], trunks["CLIP"][1], enc, "clip",
|
|
via=wf.route((3692, n + 40 + dy), after=clip_row))
|
|
wf.link(cln, src, enc, "text")
|
|
tap(wf, trunks["MODEL"], ks, "model", (TRUNK["MODEL"], lane + 12), (4152, lane + 12),
|
|
(4152, n + 40))
|
|
tap(wf, trunks["VAE"], dec, "vae", (TRUNK["VAE"], lane + 24), (4164, lane + 24),
|
|
(4164, n + 372))
|
|
wf.link(pos, "CONDITIONING", ks, "positive")
|
|
wf.link(neg, "CONDITIONING", ks, "negative")
|
|
wf.link(lat, "LATENT", ks, "latent_image")
|
|
wf.link(ks, "LATENT", dec, "samples", via=wf.route(
|
|
(4516, n + 30), (4516, n + 310), (4176, n + 310), (4176, n + 370)))
|
|
if seeds:
|
|
sl = add("WP_SeedList", (4180, n + 460), (320, 390), widgets={
|
|
"base_seed": ks_seed, "control_after_generate": "randomize", "count": seeds})
|
|
wf.link(loop, "loop_config", sl, "loop_config", via=wf.route(
|
|
(508, n + 60), (508, lane + 48), (4140, lane + 48), (4140, n + 490)))
|
|
wf.link(sl, "seed", ks, "seed", via=wf.route(
|
|
(4532, n + 490), (4532, n + 420), (4172, n + 420), (4172, n + 150)))
|
|
|
|
# Pick and grid
|
|
wf.add("WP_Note", (4600, y0 + 50), (1080, 200), title=f"{title} · pick the frames", widgets={
|
|
"wp_note": note_value(
|
|
"The run **pauses** here. Click the frames you want on the sheet, then "
|
|
"**Continue** (or **Keep all**). Each keeps its label for the grid. Press **?** "
|
|
"above the node for its keys and modes.\n\n"
|
|
"[WP Image Filter](docs:wp-image-filter){button pi-filter}",
|
|
tone="icon", icon="filter", color="tip",
|
|
bookmark=view(5420, y0 + 760, 0.5, "", 10 + gi))})
|
|
filt = add("WP_ImageFilter", (4600, n), (500, 430), widgets={"wp_image_filter": FILTER})
|
|
grid_n = add("WP_LabelGrid", (5160, n), (520, 490), widgets={"wp_label_grid": grid})
|
|
save = add("SaveImage", (5740, n), (1000, 1040), widgets={"filename_prefix": prefix})
|
|
# The run pauses at the Image Filter: the picks (and their labels, carried
|
|
# on extra_1) go on to the sheet.
|
|
wf.link(dec, "IMAGE", filt, "images", via=wf.route((4550, n + 370), (4550, n + 40)))
|
|
wf.link(lab, "prompt", filt, "extra_1", via=wf.route(
|
|
(3640, n + 610), (3640, lane + 36), (4570, lane + 36), (4570, n + 200)))
|
|
wf.link(filt, "images", grid_n, "images")
|
|
wf.link(filt, "extra_1", grid_n, "labels")
|
|
wf.link(grid_n, "grid", save, "images")
|
|
return {"loop": loop, "minfo": minfo, "head": head[-1], "asm": asm, "lab": lab,
|
|
"cln": cln, "ks": ks}
|
|
|
|
|
|
def left_column(wf: Workflow, start: str, start_h: float, *, mode_y: float) -> None:
|
|
"""Start here, the Pick a mode note and the mode switch, top to bottom."""
|
|
wf.add("WP_Note", (-1260, 0), (620, start_h), title="Start here", widgets={
|
|
"wp_note": note_value(start, tone="icon", icon="flag",
|
|
bookmark=start_view(-1260, 620, start_h))})
|
|
wf.add("WP_Note", (-1260, mode_y), (620, 190), title="Pick a mode", widgets={
|
|
"wp_note": note_value(
|
|
"**Pick one**: switching a mode on switches the others off, and each row mutes its "
|
|
"own canvas group, so only the picked group runs. The checkpoint below feeds "
|
|
"all three rows along the lines on the left.\n\n"
|
|
"[WP Toggles](docs:wp-toggles){button pi-sliders-h}",
|
|
tone="icon", icon="sliders-h", color="violet",
|
|
bookmark=view(-950, mode_y + 360, 0.7, "", 4))})
|
|
|
|
|
|
def sets_and_stories() -> None:
|
|
wf = Workflow(stable_uuid("4 - Sets and stories"))
|
|
start = (
|
|
"### Sets and stories\n\n"
|
|
"One **Run** makes a set of images from one loop. Three ways to run it, one row each; "
|
|
"**WP Toggles · pick a mode** (below) switches the other two off. Each row reads "
|
|
"left to right: **Lists**, **Prompt**, **Image**, **Pick and grid**.\n\n"
|
|
"| Mode | What changes from frame to frame |\n|---|---|\n"
|
|
"| **Every frame different** | everything: a new person, outfit, place and light |\n"
|
|
"| **Photo shoot** | pose, expression and the camera. Same person, outfit, place, light |\n"
|
|
"| **Storyboard** | the story's next beat: what happens, where, the light, the shot |\n\n"
|
|
":pi-images: Each run **pauses** at **WP Image Filter**: look the frames over, click "
|
|
"the ones you want, then **Continue**. Only the picks go on the Label & Grid sheet "
|
|
"(Keep all, or wait 10 minutes, keeps them all). [WP Image Filter](docs:wp-image-filter)"
|
|
"{button pi-filter}\n\n"
|
|
"All the lists come with this workflow, in packs (bundles) on the **WP Context** nodes.\n\n"
|
|
"[Add this workflow's lists to my library](wp:library){button pi-download}\n\n"
|
|
"| Key | Jump to |\n|---|---|\n| `1` | Every frame different |\n"
|
|
"| `2` | Photo shoot |\n| `3` | Storyboard |\n\n"
|
|
+ JUMP_TIP + "\n\n" + HELP_TIP + "\n\n"
|
|
":pi-lightbulb:{#22c55e} The lists name no style, so they suit any model. **WP Model "
|
|
"Info** in each row sets the quality words: Illustrious / NoobAI by default, Pony's "
|
|
"score tags, none for Flux. Add your style to the template.\n\n"
|
|
":pi-lock: Want this for your own lists? [Keep something the same](docs:howto-keep-steady)"
|
|
"{button pi-lock} explains **Hold across run**, the switch the photo shoot and the "
|
|
"storyboard lean on." + LINKS_FOOTER)
|
|
left_column(wf, start, 1140, mode_y=1240)
|
|
wf.add("WP_Toggles", (-1260, 1460), (560, 230), title="WP Toggles · pick a mode",
|
|
widgets={"wp_toggles": json.dumps({
|
|
"version": 1, "mode": "one", "group_var": "pack_mode",
|
|
"rows": [
|
|
{"id": "a1f0c001", "kind": "toggle", "name": GROUPS[0][0],
|
|
"var": "mode_random", "on": False, "pin": False, "out": 0,
|
|
"style": "switch", "control": "mute", "groups": [GROUPS[0][0]],
|
|
"value": "random"},
|
|
{"id": "a1f0c002", "kind": "toggle", "name": GROUPS[1][0],
|
|
"var": "mode_shoot", "on": False, "pin": False, "out": 0,
|
|
"style": "switch", "control": "mute", "groups": [GROUPS[1][0]],
|
|
"value": "shoot"},
|
|
{"id": "a1f0c003", "kind": "toggle", "name": GROUPS[2][0],
|
|
"var": "mode_story", "on": True, "pin": False, "out": 0,
|
|
"style": "switch", "control": "mute", "groups": [GROUPS[2][0]],
|
|
"value": "story"},
|
|
]})})
|
|
ckpt = wf.add("CheckpointLoaderSimple", (-1260, 1760), (560, 100),
|
|
widgets={"ckpt_name": "sd_xl_base_1.0.safetensors"})
|
|
trunks = bus(wf, ckpt, {"MODEL": ("MODEL", 1786), "CLIP": ("CLIP", 1798),
|
|
"VAE": ("VAE", 1810)})
|
|
|
|
for gi, (gtitle, gcolor) in enumerate(GROUPS):
|
|
salt = f"sets-{gi}"
|
|
if gi == 0:
|
|
contexts = [
|
|
("WP Context · Person", 1, [photo.render(False), photo.person()]),
|
|
("WP Context · Wardrobe and place", 2, [photo.location(), photo.wardrobe()]),
|
|
("WP Context · Light and camera", 3,
|
|
[photo.lighting(), photo.camera(), photo.direction()]),
|
|
]
|
|
kw = dict(loop_values={"seed": 11, "count": 8, "wp_context_loop_config": loop_config()},
|
|
prompt=photo.PROMPT, label="$age $gender · $place",
|
|
grid=grid_config("Every frame different", label=True, columns=4),
|
|
text=("A **Context Loop** of 8 frames. Nothing is held, so every frame "
|
|
"rolls the whole chain again: a new person, outfit, place, light "
|
|
"and shot.\n\n"
|
|
"[WP Context Loop](docs:wp-context-loop){button pi-replay}"),
|
|
prompt_note=("One prompt per frame. The labels Assembler under the "
|
|
"Cleaner writes `$age $gender · $place` for the sheet."),
|
|
image_note=("**WP Seed List** gives each frame its own sampler seed, "
|
|
"following the loop."),
|
|
seeds=8)
|
|
elif gi == 1:
|
|
cam = photo.camera(hold_film=True)
|
|
shot = cam.children[0]
|
|
assert isinstance(shot, Module) and shot.name == "Shot"
|
|
order = ["wide shot", "full body", "cowboy shot", "upper body",
|
|
"portrait", "close-up"]
|
|
shot.iteration_overrides = {
|
|
str(i): {"enabled_options": [shot.option_id(v)]} for i, v in enumerate(order)
|
|
}
|
|
contexts = [
|
|
("WP Context · Person (held)", 4, [photo.render(False), photo.person(hold=True)]),
|
|
("WP Context · Wardrobe and place (held)", 5,
|
|
[photo.location(hold=True), photo.wardrobe(hold=True),
|
|
photo.lighting(hold=True)]),
|
|
("WP Context · Shot list", 6, [cam, photo.direction()]),
|
|
]
|
|
kw = dict(loop_values={"seed": 21, "count": 6, "wp_context_loop_config": loop_config()},
|
|
prompt=photo.PROMPT, label="$shot",
|
|
grid=grid_config("Photo shoot: one person, six shots", label=True,
|
|
columns=3),
|
|
text=("Every row in the first two Contexts has **Hold across run** on "
|
|
"(the pin icon): one person, outfit, place and light for the whole "
|
|
"set.\n\nThe *Shot* row has a pick **per frame** (Edit frame chips "
|
|
"#1 to #6 in the Shot list Context): wide, full body, three "
|
|
"quarter, waist up, head and shoulders, close-up. Pose and "
|
|
"expression still roll each frame.\n\n"
|
|
"[Per-iteration overrides](docs:iteration-overrides){button pi-images}"),
|
|
prompt_note="The labels Assembler writes `$shot`, so the sheet names "
|
|
"each frame's framing.",
|
|
image_note="The sampler seed is **fixed**, so the face drifts less "
|
|
"between shots.",
|
|
seeds=None)
|
|
else:
|
|
film = photo.held(photo.film)
|
|
contexts = [
|
|
("WP Context · Person (held)", 7, [photo.render(False), photo.person(hold=True)]),
|
|
("WP Context · Story and wardrobe", 8, [
|
|
Bundle("Story", "#b06634", [stories.story, stories.story_outfit_rule],
|
|
scope=photo.S),
|
|
photo.held_all(Bundle("Wardrobe", "#c08a3e",
|
|
[photo.gender_outfit_rule, photo.outfit,
|
|
photo.accessories, photo.shoes],
|
|
scope=photo.S))]),
|
|
("WP Context · Beats", 9, [
|
|
Bundle("Beats", "#b06634", [stories.beats()], scope=photo.S),
|
|
Bundle("Camera", "#5a7fc9", [photo.lens_from_shot, film, photo.framing_rule],
|
|
scope=photo.S)]),
|
|
]
|
|
kw = dict(loop_values={"seed": 31, "count": 6, "wp_context_loop_config": loop_config()},
|
|
prompt=stories.STORY_PROMPT, label="$beat_title",
|
|
grid=grid_config("Storyboard", label=True, columns=3),
|
|
text=("A held **Story** picks one of four scripts for the run. **Beats** "
|
|
"is a derivation: *IF $story is … AND $iteration is 3* it writes "
|
|
"that frame's action, place, light, shot and expression. The gala "
|
|
"story changes outfit at the first beat.\n\n"
|
|
"[Derivation](docs:derivation){button pi-share-alt} "
|
|
"[Seeds & loops](docs:seeds-and-loops){button pi-refresh}"),
|
|
prompt_note="The labels Assembler writes `$beat_title`, so the sheet "
|
|
"reads as the story, in order.",
|
|
image_note="Person and wardrobe are held and the sampler seed is "
|
|
"**fixed**, so the same people carry the story.",
|
|
seeds=None)
|
|
mode_row(wf, gi, title=gtitle, color=gcolor, muted=MUTE if gi != 2 else 0,
|
|
trunks=trunks, contexts=contexts, salt=salt, negative=photo.NEGATIVE,
|
|
cleaner=CLEANER, ks_seed=42 + gi,
|
|
prefix=f"wp-examples/{gtitle.lower().replace(' ', '-')}", **kw)
|
|
|
|
wf.save(OUT / "4 - Sets and stories.json", (0.16, 1500, 500))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import core_workflows
|
|
|
|
core_workflows.build_all()
|
|
print("wrote", OUT)
|