diff --git a/artifacts/cards/BP-001-moses/README.md b/artifacts/cards/BP-001-moses/README.md new file mode 100644 index 0000000..194e638 --- /dev/null +++ b/artifacts/cards/BP-001-moses/README.md @@ -0,0 +1,49 @@ +# BP-001 — Moses — v05 + +Assembly-review candidate for the bespoke Legendary Moses card. The selected v05 illustration and the `vertical-text-prototype-v7` composition are frozen as the production basis. + +## Selected composition + +- Upper-right nameplate beneath the cloud crown. +- Short left tent-textile verse backing with a shallow pitched seam and restrained linen texture. +- The card and backing remain visually forward; the source cloud crown and complete folded blue sleeve with red lining cross their nearby seams. +- The separate lower red garment shard remains behind the backing. +- Four semantic verse lines use P052 Medium, followed by `Deuteronomy 34:10 • ESV`. +- No embroidery is included. + +## Production outputs + +Each resolution contains `normal`, `boundless`, `borderless`, and `textless` printings: + +- `low`: 500 × 700 +- `med`: 1000 × 1400 +- `high`: 2000 × 2800 + +Every printing includes `card.png` and `finish-mask.png`. Normal and Borderless also include `text-mask.png`. The high-resolution Normal face is byte-identical to the approved v7 prototype. + +Printing composition: + +- **Normal:** selected complete composition. +- **Boundless:** selected illustration only. +- **Borderless:** nameplate, tent textile, adapted cloud/sleeve foreground, and text without the perimeter frame. +- **Textless:** selected illustration and bespoke perimeter frame without backings or lettering; the tent roof and cloud crown pass in front of the top-left rail, which resumes over open sky. + +Finish masks use the shared `raw-ridges-v1` recipe. Coverage is resolved in visible depth order, so the adapted cloud and sleeve retain illustration finish while opaque frame, backing, and lettering regions suppress it. Text masks come from rendered glyph alpha with the standard expansion and soft edge. + +## Review + +- `review/printings-comparison.png` +- `review/layers-comparison.png` +- `review/finish-masks-comparison.png` +- `review/textile-finish-detail.png` +- `review/build-validation.json` +- `review/static-review.json` + +Rebuild and validate from the repository root: + +```sh +python3 in-progress/cards/BP-001-moses/revisions/v05/build.py +python3 tools/card-production/card_workspace.py validate --card BP-001 --revision v05 +``` + +The revision is at `assembly-review`. Final card approval and promotion have not been recorded. diff --git a/artifacts/cards/BP-001-moses/build.py b/artifacts/cards/BP-001-moses/build.py new file mode 100644 index 0000000..985f96e --- /dev/null +++ b/artifacts/cards/BP-001-moses/build.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +"""Build BP-001 Moses v05 as a bespoke Legendary card.""" +from __future__ import annotations + +from copy import deepcopy +from pathlib import Path +import hashlib +import json +import os +import shutil +import subprocess +import sys +import xml.etree.ElementTree as ET +from xml.sax.saxutils import escape + +import numpy as np +from PIL import Image, ImageDraw, ImageFilter, ImageFont + + +ROOT = Path(__file__).resolve().parent +REPO = next(p for p in ROOT.parents if (p / "docs/card-layer-pipeline.md").is_file()) +sys.path.insert(0, str(REPO / "tools/card-production")) + +from card_workspace import refresh, update_index +from finish_masks import RECIPE_PATH, RECIPE_SHA256, load_recipe, prepare_illustration + + +CANVAS = (2000, 2800) +PRINTINGS = ("normal", "boundless", "borderless", "textless") +RESOLUTIONS = (("low", 500), ("med", 1000), ("high", 2000)) +PROOF = ROOT / "review/vertical-text-prototype-v7" + + +def digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def save_json(path: Path, value: dict) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, indent=2) + "\n") + + +def check(condition: bool, message: str) -> None: + if not condition: + raise ValueError(message) + + +def alpha(image: Image.Image) -> np.ndarray: + check("A" in image.getbands(), "Component must carry alpha") + return np.asarray(image.getchannel("A"), dtype=np.uint8) + + +def split_overlay_svg(source: Path, frame_output: Path, backing_output: Path) -> None: + """Split the selected proof's combined SVG without redrawing its geometry.""" + tree = ET.parse(source) + root = tree.getroot() + children = list(root) + check(children and children[0].tag.endswith("defs"), "Selected overlay SVG lacks defs") + + def write(destination: Path, want_frame: bool) -> None: + result = ET.Element(root.tag, root.attrib) + result.append(deepcopy(children[0])) + for child in children[1:]: + is_frame = child.attrib.get("id") == "legendary-frame" + if is_frame == want_frame: + result.append(deepcopy(child)) + ET.register_namespace("", "http://www.w3.org/2000/svg") + ET.register_namespace("xlink", "http://www.w3.org/1999/xlink") + destination.parent.mkdir(parents=True, exist_ok=True) + ET.ElementTree(result).write(destination, encoding="unicode") + + write(frame_output, True) + write(backing_output, False) + + +def main() -> None: + card_path = ROOT / "card.json" + manifest_path = ROOT / "manifest.json" + card = json.loads(card_path.read_text()) + manifest = json.loads(manifest_path.read_text()) + art_path = ROOT / "source/art-master.png" + art = Image.open(art_path).convert("RGBA") + check(art.size == CANVAS and art.getchannel("A").getextrema() == (255, 255), "Selected art must be opaque 2000x2800") + check(digest(art_path) == card["artSHA256"], "Selected art hash changed") + + selected = { + "normal": PROOF / "composed-preview-2000.png", + "overlaySvg": PROOF / "border-backing-preview.svg", + "foreground": PROOF / "foreground-curtain.png", + "foregroundSvg": PROOF / "foreground-curtain.svg", + "text": PROOF / "text-preview.png", + "textSvg": PROOF / "text-preview.svg", + "validation": PROOF / "preview-validation.json", + } + for path in selected.values(): + check(path.is_file(), f"Missing selected v7 proof input: {path}") + proof_validation = json.loads(selected["validation"].read_text()) + check(proof_validation["status"] == "passed", "Selected v7 proof did not pass") + check(proof_validation["structuralChecks"]["curtainTextIntersectionPixels"] == 0, "Selected foreground intersects text") + check(proof_validation["structuralChecks"]["glyphPixelsOutsideOpaqueBacking"] == 0, "Selected text escapes backing") + + fonts = REPO / "fonts" + font_manifest_path = fonts / "manifest.json" + font_manifest = json.loads(font_manifest_path.read_text()) + title_font = fonts / "P052-Bold.otf" + body_font = fonts / "SanctificationP052-Medium.otf" + for font in (title_font, body_font): + check(digest(font) == font_manifest["files"][font.name]["sha256"], f"Font hash mismatch: {font.name}") + + review = ROOT / "review" + layers = ROOT / "source/layers" + text_dir = ROOT / "source/text" + foreground_dir = ROOT / "source/foreground" + material_dir = ROOT / "source/material" + for directory in (review, layers, text_dir, foreground_dir, material_dir): + directory.mkdir(parents=True, exist_ok=True) + + font_cache = review / "font-cache" + font_cache.mkdir(exist_ok=True) + fonts_conf = review / "fonts.conf" + fonts_conf.write_text(f"{fonts}{font_cache}") + env = dict(os.environ, FONTCONFIG_FILE=str(fonts_conf)) + + def ink(*args: object) -> str: + return subprocess.check_output(["inkscape", *map(str, args)], env=env, text=True, stderr=subprocess.PIPE).strip() + + def render(source: Path, destination: Path) -> None: + destination.parent.mkdir(parents=True, exist_ok=True) + ink(source, "--export-type=png", f"--export-filename={destination}", "--export-width=2000", "--export-height=2800") + + resolved_title = Path(subprocess.check_output(["fc-match", "-f", "%{file}", "P052:style=Bold"], env=env, text=True).strip()).resolve() + resolved_body = Path(subprocess.check_output(["fc-match", "-f", "%{file}", "Sanctification P052:style=Medium"], env=env, text=True).strip()).resolve() + check(resolved_title == title_font.resolve(), "P052 Bold fallback detected") + check(resolved_body == body_font.resolve(), "P052 Medium fallback detected") + + # Freeze the selected proof components as production inputs. + shutil.copy2(selected["normal"], ROOT / "source/selected-normal-2000.png") + shutil.copy2(selected["foreground"], foreground_dir / "foreground-2000.png") + shutil.copy2(selected["foregroundSvg"], foreground_dir / "selected-foreground.svg") + shutil.copy2(selected["text"], text_dir / "text-2000.png") + shutil.copy2(selected["textSvg"], text_dir / "selected-text.svg") + shutil.copy2(selected["overlaySvg"], layers / "selected-overlay.svg") + + frame_svg = layers / "frame.svg" + backing_svg = layers / "backing.svg" + split_overlay_svg(selected["overlaySvg"], frame_svg, backing_svg) + render(frame_svg, layers / "frame-2000.png") + render(backing_svg, layers / "backing-2000.png") + + frame = Image.open(layers / "frame-2000.png").convert("RGBA") + backing = Image.open(layers / "backing-2000.png").convert("RGBA") + foreground = Image.open(foreground_dir / "foreground-2000.png").convert("RGBA") + text = Image.open(text_dir / "text-2000.png").convert("RGBA") + for name, component in (("frame", frame), ("backing", backing), ("foreground", foreground), ("text", text)): + check(component.size == CANVAS, f"{name} is not registered to the master canvas") + + # The source curtain sits in front of the outer left rail, but below the backing. + rail_mask = Image.new("L", CANVAS, 0) + ImageDraw.Draw(rail_mask).polygon([(0, 0), (80, 0), (80, 2646), (0, 2640)], fill=255) + rail = art.copy() + rail.putalpha(rail_mask) + rail.save(foreground_dir / "curtain-over-rail-2000.png") + + # Textless keeps the scene's depth at the top-left corner: the tent roof + # and cloud crown occlude the perimeter rail until it reaches open sky. + # The selected foreground already carries that approved source contour; + # crop its alpha to the upper crown so the sleeve is not duplicated here. + crown_alpha_image = foreground.getchannel("A") + crown_alpha_values = np.asarray(crown_alpha_image).copy() + crown_alpha_values[600:, :] = 0 + cloud_crown = foreground.copy() + cloud_crown.putalpha(Image.fromarray(crown_alpha_values, "L")) + cloud_crown.save(foreground_dir / "cloud-crown-over-frame-2000.png") + textless_occlusion = Image.alpha_composite(rail, cloud_crown) + textless_occlusion.save(foreground_dir / "textless-frame-occlusion-2000.png") + + combined_overlay = Image.alpha_composite(frame, backing) + combined_overlay.save(layers / "overlay-normal-2000.png") + backing.save(layers / "backing-borderless-2000.png") + frame.save(layers / "frame-textless-2000.png") + + frame_a = alpha(frame).astype(np.float32) / 255.0 + backing_a = alpha(backing).astype(np.float32) / 255.0 + foreground_a = alpha(foreground).astype(np.float32) / 255.0 + rail_a = alpha(rail).astype(np.float32) / 255.0 + crown_a = alpha(cloud_crown).astype(np.float32) / 255.0 + text_a = alpha(text).astype(np.float32) / 255.0 + check(not np.any((text_a > 0) & (foreground_a > 0)), "Production foreground intersects text") + check(np.all(alpha(backing)[text_a > 0] == 255), "Production text escapes opaque backing") + + selected_normal = Image.open(ROOT / "source/selected-normal-2000.png").convert("RGBA") + check(selected_normal.size == CANVAS and selected_normal.getchannel("A").getextrema() == (255, 255), "Selected Normal is invalid") + + # Reconstruct the selected order as a diagnostic. The selected PNG remains + # authoritative because the approved proof pins inherited antialias pixels. + reconstructed = Image.alpha_composite(art, frame) + reconstructed = Image.alpha_composite(reconstructed, rail) + reconstructed = Image.alpha_composite(reconstructed, backing) + reconstructed = Image.alpha_composite(reconstructed, foreground) + reconstructed = Image.alpha_composite(reconstructed, text) + reconstructed.save(review / "normal-reconstructed.png") + delta = np.abs(np.asarray(selected_normal, dtype=np.int16)[:, :, :3] - np.asarray(reconstructed, dtype=np.int16)[:, :, :3]) + changed = np.any(delta > 0, axis=2) + + faces = { + "normal": selected_normal, + "boundless": art.copy(), + "borderless": Image.alpha_composite(Image.alpha_composite(Image.alpha_composite(art, backing), foreground), text), + "textless": Image.alpha_composite(Image.alpha_composite(art, frame), textless_occlusion), + } + check(np.array_equal(np.asarray(faces["boundless"]), np.asarray(art)), "Boundless must equal selected art") + expected_textless = Image.alpha_composite(Image.alpha_composite(art, frame), textless_occlusion) + check(np.array_equal(np.asarray(faces["textless"]), np.asarray(expected_textless)), "Textless component error") + crown_opaque = crown_a == 1.0 + check(np.array_equal(np.asarray(faces["textless"])[crown_opaque], np.asarray(cloud_crown)[crown_opaque]), "Tent/cloud crown does not fully occlude the Textless frame") + check(np.any(frame_a[:100, 1200:] > 0), "Textless top rail no longer resumes over open sky") + + # Expand and soften actual glyph alpha for the runtime readability mask. + protected_text = text.getchannel("A").filter(ImageFilter.MaxFilter(25)).filter(ImageFilter.GaussianBlur(8)) + check(int(np.min(np.asarray(protected_text)[text_a >= 0.5])) >= 216, "Text protection is too weak over rendered glyphs") + + # Analyze the illustration and the source-registered adapted foreground once. + base_analysis = prepare_illustration(art) + adapted_plate = Image.alpha_composite(art, foreground) + adapted_analysis = prepare_illustration(adapted_plate) + adapted_plate.save(material_dir / "foreground-analysis-plate.png") + Image.fromarray(base_analysis.raw_ridges).save(material_dir / "base-raw-ridges.png") + Image.fromarray(np.rint(base_analysis.pane_weights * 255).astype(np.uint8)).save(material_dir / "base-pane-coverage.png") + Image.fromarray(adapted_analysis.raw_ridges).save(material_dir / "foreground-raw-ridges.png") + Image.fromarray(np.rint(adapted_analysis.pane_weights * 255).astype(np.uint8)).save(material_dir / "foreground-pane-coverage.png") + + def coverage(analysis) -> np.ndarray: + return analysis.pane_weights * (1.0 - analysis.raw_ridges.astype(np.float32) / 255.0) + + base_coverage = coverage(base_analysis) + adapted_coverage = coverage(adapted_analysis) + + def suppress(values: np.ndarray, component_alpha: np.ndarray) -> np.ndarray: + return values * (1.0 - component_alpha) + + def restore(values: np.ndarray, component_alpha: np.ndarray, component_coverage: np.ndarray) -> np.ndarray: + return values * (1.0 - component_alpha) + component_coverage * component_alpha + + finish = {} + # Resolve each printing in its actual front-to-back order. + values = suppress(base_coverage, frame_a) + values = restore(values, rail_a, base_coverage) + values = suppress(values, backing_a) + values = restore(values, foreground_a, adapted_coverage) + finish["normal"] = suppress(values, text_a) + + values = suppress(base_coverage, backing_a) + values = restore(values, foreground_a, adapted_coverage) + finish["borderless"] = suppress(values, text_a) + values = restore(suppress(base_coverage, frame_a), rail_a, base_coverage) + finish["textless"] = restore(values, crown_a, adapted_coverage) + finish["boundless"] = base_coverage + + head = '' + + def image_node(editable: Path, source: Path) -> str: + return f'' + + printing_components = { + "normal": ["base-art", "frame", "curtain-over-rail", "backing", "adapted-foreground", "text"], + "boundless": ["base-art"], + "borderless": ["base-art", "backing", "adapted-foreground", "text"], + "textless": ["base-art", "frame", "curtain-over-rail", "cloud-crown-over-frame"], + } + report = { + "status": "pending", + "cardId": card["cardId"], + "revision": manifest["revision"], + "finalCardApproval": None, + "assemblyAuthorization": "Okay. Let's finish the Moses card with the actual generating the prints/masks", + "selectedPrototype": { + "path": str(PROOF.relative_to(REPO)), + "normalSHA256": digest(selected["normal"]), + "validationSHA256": digest(selected["validation"]), + "authority": "v7 comparison PNG; inherited antialias pixels intentionally pinned by the selected proof", + }, + "inputs": { + "art": {"path": str(art_path.relative_to(REPO)), "sha256": digest(art_path)}, + "foreground": {"path": str((foreground_dir / "foreground-2000.png").relative_to(REPO)), "sha256": digest(foreground_dir / "foreground-2000.png")}, + "text": {"path": str((text_dir / "text-2000.png").relative_to(REPO)), "sha256": digest(text_dir / "text-2000.png")}, + "builderSHA256": digest(Path(__file__)), + }, + "bespokeLayers": { + "template": "BP-001-moses-legendary-tent-textile-v1", + "canvas": list(CANVAS), + "compositionOrder": ["base-art", "frame", "curtain-over-rail", "backing", "adapted-cloud-and-sleeve", "text"], + "printingComponents": printing_components, + }, + "typography": { + "sharedFontManifestSHA256": digest(font_manifest_path), + "title": {"font": "P052-Bold.otf", "weight": 700, "size": 142}, + "verse": {"font": "SanctificationP052-Medium.otf", "weight": 500, "size": 66, "lines": [ + "And there has not arisen", "a prophet since in Israel", "like Moses, whom the LORD", "knew face to face" + ]}, + "reference": {"font": "SanctificationP052-Medium.otf", "weight": 500, "size": 48, "text": card["referenceDisplay"]}, + "proofChecks": proof_validation["typography"], + }, + "textMask": {"expansionRadius": 12, "gaussianRadius": 8, "units": "master pixels"}, + "finishRecipe": { + "id": load_recipe()["id"], + "sha256": RECIPE_SHA256, + "path": str(RECIPE_PATH.relative_to(REPO)), + "baseAnalysisCount": 1, + "adaptedForegroundAnalysisCount": 1, + }, + "normalAuthorityCheck": { + "productionEqualsSelectedPrototype": True, + "selectedSHA256": digest(selected["normal"]), + "productionSHA256": None, + "reconstructionChangedPixels": int(np.count_nonzero(changed)), + "reconstructionMaximumChannelDelta": int(delta.max()), + }, + "printings": {}, + "tools": {"inkscape": ink("--version"), "pillow": Image.__version__, "numpy": np.__version__}, + "visualReview": {"static": "pending user review", "movingLight": "not performed", "userApproval": "pending"}, + } + + source_paths = { + "normal": ROOT / "source/selected-normal-2000.png", + "boundless": art_path, + "borderless": None, + "textless": None, + } + for printing in PRINTINGS: + face = faces[printing] + check(face.getchannel("A").getextrema() == (255, 255), f"{printing} face is not opaque") + finish_master = Image.fromarray(np.rint(np.clip(finish[printing], 0.0, 1.0) * 255).astype(np.uint8), "L") + has_text = printing in ("normal", "borderless") + exports = [] + for label, width in RESOLUTIONS: + dimensions = (width, width * 7 // 5) + folder = ROOT / label / printing + folder.mkdir(parents=True, exist_ok=True) + if printing == "normal" and width == 2000: + # The selected proof PNG is the comparison authority. Preserve + # its original RGB encoding as well as its pixels. + shutil.copy2(selected["normal"], folder / "card.png") + else: + output_face = face if width == 2000 else face.resize(dimensions, Image.Resampling.LANCZOS) + output_face.save(folder / "card.png") + output_finish = finish_master if width == 2000 else finish_master.resize(dimensions, Image.Resampling.BILINEAR) + output_finish.convert("RGBA").save(folder / "finish-mask.png") + if has_text: + output_text = protected_text if width == 2000 else protected_text.resize(dimensions, Image.Resampling.BILINEAR) + output_text.convert("RGBA").save(folder / "text-mask.png") + names = ["card.png", "finish-mask.png"] + (["text-mask.png"] if has_text else []) + exports.append({"resolution": label, "dimensions": list(dimensions), "files": {name: digest(folder / name) for name in names}}) + + editable = ROOT / "high" / printing / "card.svg" + if source_paths[printing] is not None: + body = image_node(editable, source_paths[printing]) + else: + body = image_node(editable, art_path) + if printing == "borderless": + body += image_node(editable, layers / "backing-2000.png") + body += image_node(editable, foreground_dir / "foreground-2000.png") + body += image_node(editable, text_dir / "text-2000.png") + elif printing == "textless": + body += image_node(editable, layers / "frame-2000.png") + body += image_node(editable, foreground_dir / "textless-frame-occlusion-2000.png") + editable.write_text(head + body + "") + report["printings"][printing] = { + "components": printing_components[printing], + "textMask": has_text, + "finishCoverage": "base plus depth-resolved adapted foreground" if printing in ("normal", "borderless") else "base illustration with frame depth resolved" if printing == "textless" else "base illustration", + "exports": exports, + } + + report["normalAuthorityCheck"]["productionSHA256"] = digest(ROOT / "high/normal/card.png") + check(report["normalAuthorityCheck"]["productionSHA256"] == report["normalAuthorityCheck"]["selectedSHA256"], "Normal export changed from selected v7 proof") + + save_json(layers / "layout.json", { + "id": "BP-001-moses-legendary-tent-textile-v1", + "canvas": list(CANVAS), + "selectedProof": str(PROOF.relative_to(REPO)), + "titleBacking": "upper-right paper plaque beneath the source cloud crown", + "verseBacking": "shortened left tent textile with shallow pitched seam", + "depthOrder": ["base-art", "frame", "curtain-over-rail", "backings", "cloud-crown-and-complete-blue-red-sleeve", "text"], + "printingComponents": printing_components, + "foregroundScope": "Normal and Borderless restore the source cloud crown plus complete blue outer sleeve and red inner lining ending at the low V. Textless restores the tent/cloud crown over the top-left rail. The separate lower red shard stays behind the backing.", + }) + save_json(ROOT / "source/typography-layout.json", { + "canvas": list(CANVAS), + "policy": "Bespoke Legendary coordinates; semantic four-line verse; actual ink centered in the reserved band.", + "titleAnchor": [1458, 244], + "verseAnchorX": 530, + "verseBaselines": [2189, 2299, 2409, 2519], + "referenceBaseline": 2640, + "fontSizes": {"title": 142, "verse": 66, "reference": 48}, + "verseLines": report["typography"]["verse"]["lines"], + "reference": card["referenceDisplay"], + "measured": { + "verseInkUnion": proof_validation["typography"]["verseInkUnion"], + "upperFlourishToVerseInk": proof_validation["typography"]["upperFlourishToVerseInk"], + "verseInkToReferenceLineBox": proof_validation["typography"]["verseInkToReferenceLineBox"], + "centeringDifference": proof_validation["typography"]["centeringDifference"], + }, + }) + save_json(ROOT / "source/production-inputs.json", { + "selectedProof": str(PROOF.relative_to(REPO)), + "files": {name: {"path": str(path.relative_to(REPO)), "sha256": digest(path)} for name, path in selected.items()}, + "fonts": {font.name: digest(font) for font in (title_font, body_font)}, + "finishRecipeSHA256": RECIPE_SHA256, + }) + + # Review sheets. + label_font = ImageFont.truetype(str(title_font), 24) + for filename, item_name in (("printings-comparison.png", "card.png"), ("finish-masks-comparison.png", "finish-mask.png")): + sheet = Image.new("RGB", (1600, 610), "#11161c") + draw = ImageDraw.Draw(sheet) + for index, printing in enumerate(PRINTINGS): + draw.text((index * 400 + 14, 12), printing.title(), font=label_font, fill="#f4ead4") + image = Image.open(ROOT / "med" / printing / item_name).convert("RGB") + image.thumbnail((370, 518), Image.Resampling.LANCZOS if item_name == "card.png" else Image.Resampling.BILINEAR) + sheet.paste(image, (index * 400 + 14, 56)) + sheet.save(review / filename) + + checker = Image.new("RGBA", CANVAS, (30, 34, 40, 255)) + checker_values = np.asarray(checker).copy() + for y in range(0, 2800, 100): + for x in range(0, 2000, 100): + if (x // 100 + y // 100) % 2: + checker_values[y:y + 100, x:x + 100, :3] = [56, 61, 69] + checker = Image.fromarray(checker_values, "RGBA") + items = [("Frame", frame), ("Backings", backing), ("Textless depth", textless_occlusion), ("Cloud + sleeve", foreground), ("Text", text)] + sheet = Image.new("RGB", (1500, 500), "#11161c") + draw = ImageDraw.Draw(sheet) + for index, (label, layer) in enumerate(items): + draw.text((index * 300 + 10, 8), label, font=label_font, fill="#f4ead4") + composed = checker.copy() + composed.alpha_composite(layer) + composed.thumbnail((280, 392), Image.Resampling.LANCZOS) + sheet.paste(composed.convert("RGB"), (index * 300 + 10, 50)) + sheet.save(review / "layers-comparison.png") + + detail = Image.new("RGB", (1500, 760), "#11161c") + draw = ImageDraw.Draw(detail) + for index, (label, path) in enumerate([ + ("Normal card", ROOT / "high/normal/card.png"), + ("Normal finish", ROOT / "high/normal/finish-mask.png"), + ("Text protection", ROOT / "high/normal/text-mask.png"), + ]): + draw.text((index * 500 + 12, 12), label, font=label_font, fill="#f4ead4") + image = Image.open(path).convert("RGB").crop((0, 1500, 1200, 2800)) + image.thumbnail((480, 690), Image.Resampling.LANCZOS) + detail.paste(image, (index * 500 + 10, 48)) + detail.save(review / "textile-finish-detail.png") + + report["status"] = "passed" + save_json(review / "build-validation.json", report) + save_json(review / "static-review.json", { + "status": "passed-structural-user-visual-review-pending", + "cardId": card["cardId"], + "revision": manifest["revision"], + "checks": { + "selectedNormalPreservedByteForByte": True, + "fourPrintingComposition": "passed", + "layerRegistration": "passed", + "opaqueFaces": "passed", + "textOnOpaqueBacking": "passed", + "foregroundTextIntersectionPixels": 0, + "depthResolvedFinishCoverage": "passed", + "textlessTentCloudOccludesTopLeftRail": "passed", + "textlessTopRailResumesOverOpenSky": "passed", + "lowMedHighExports": "passed", + }, + "reviewAssets": ["printings-comparison.png", "layers-comparison.png", "finish-masks-comparison.png", "textile-finish-detail.png"], + "staticVisualAssessment": "Pending user review of the four production printings and masks.", + "movingLightReview": "Not performed.", + "finalCardApproval": None, + }) + + card.update({ + "artApproval": {"by": "user", "scope": "v05 source art and selected v7 Legendary Normal composition"}, + "artStatus": "Selected v05 illustration approved for production assembly", + "layoutStatus": "Bespoke Legendary tent-textile production candidate assembled; four printings and masks await user review.", + "template": "BP-001-moses-legendary-tent-textile-v1", + "printingComponents": printing_components, + "typographyCandidate": { + "titleFontSize": 142, + "verseFontSize": 66, + "verseBaselineGap": 110, + "referenceFontSize": 48, + "lineBreaks": report["typography"]["verse"]["lines"], + }, + "finishRecipe": "raw-ridges-v1", + "assemblyAuthorization": {"by": "user", "note": report["assemblyAuthorization"]}, + }) + save_json(card_path, card) + manifest["stage"] = "assembly-review" + manifest["approval"] = None + save_json(manifest_path, manifest) + profile_path = ROOT.parents[1] / "card.json" + profile = json.loads(profile_path.read_text()) + profile.update({"stage": "assembly-review", "selectedRevision": manifest["revision"], "approval": None}) + save_json(profile_path, profile) + refresh(ROOT) + update_index() + print(f"{card['cardId']} {manifest['revision']}: four Legendary printings and production masks exported; structural validation passed.") + + +if __name__ == "__main__": + main() diff --git a/artifacts/cards/BP-001-moses/card.json b/artifacts/cards/BP-001-moses/card.json new file mode 100644 index 0000000..8aeb48e --- /dev/null +++ b/artifacts/cards/BP-001-moses/card.json @@ -0,0 +1,80 @@ +{ + "cardId": "BP-001", + "title": "Moses", + "name": "MOSES", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "artApproval": { + "by": "user", + "scope": "v05 source art and selected v7 Legendary Normal composition" + }, + "reference": "Deuteronomy 34:10", + "translation": "ESV", + "referenceDisplay": "Deuteronomy 34:10 \u2022 ESV", + "excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face", + "direction": "The Threshold", + "artStatus": "Selected v05 illustration approved for production assembly", + "layoutStatus": "Bespoke Legendary tent-textile production candidate assembled; four printings and masks await user review.", + "sceneConstraints": [ + "Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.", + "Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.", + "No Red Sea or Sinai action; these belong to event cards." + ], + "compositionApproval": { + "by": "user", + "scope": "v01 composition only; visual rendering requested to change" + }, + "revisionIntent": "Continue the near golden post through the cloud to ground using a deterministic narrow authored overlay.", + "art": "artifacts/cards/BP-001-moses/source/art-master.png", + "artSHA256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "template": "BP-001-moses-legendary-tent-textile-v1", + "printingComponents": { + "normal": [ + "base-art", + "frame", + "curtain-over-rail", + "backing", + "adapted-foreground", + "text" + ], + "boundless": [ + "base-art" + ], + "borderless": [ + "base-art", + "backing", + "adapted-foreground", + "text" + ], + "textless": [ + "base-art", + "frame", + "curtain-over-rail", + "cloud-crown-over-frame" + ] + }, + "typographyCandidate": { + "titleFontSize": 142, + "verseFontSize": 66, + "verseBaselineGap": 110, + "referenceFontSize": 48, + "lineBreaks": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ] + }, + "finishRecipe": "raw-ridges-v1", + "assemblyAuthorization": { + "by": "user", + "note": "Okay. Let's finish the Moses card with the actual generating the prints/masks" + }, + "schemaVersion": 1, + "selectedRevision": "v05", + "stage": "approved", + "approval": { + "by": "user", + "note": "This looks great. This iteration is approved" + } +} diff --git a/artifacts/cards/BP-001-moses/high/borderless/card.png b/artifacts/cards/BP-001-moses/high/borderless/card.png new file mode 100644 index 0000000..ebcb63b Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/borderless/card.png differ diff --git a/artifacts/cards/BP-001-moses/high/borderless/card.svg b/artifacts/cards/BP-001-moses/high/borderless/card.svg new file mode 100644 index 0000000..0294ec8 --- /dev/null +++ b/artifacts/cards/BP-001-moses/high/borderless/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/high/borderless/finish-mask.png b/artifacts/cards/BP-001-moses/high/borderless/finish-mask.png new file mode 100644 index 0000000..9fce1c1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/borderless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/high/borderless/text-mask.png b/artifacts/cards/BP-001-moses/high/borderless/text-mask.png new file mode 100644 index 0000000..196f965 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/borderless/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/high/boundless/card.png b/artifacts/cards/BP-001-moses/high/boundless/card.png new file mode 100644 index 0000000..e6791a7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/boundless/card.png differ diff --git a/artifacts/cards/BP-001-moses/high/boundless/card.svg b/artifacts/cards/BP-001-moses/high/boundless/card.svg new file mode 100644 index 0000000..166b864 --- /dev/null +++ b/artifacts/cards/BP-001-moses/high/boundless/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/high/boundless/finish-mask.png b/artifacts/cards/BP-001-moses/high/boundless/finish-mask.png new file mode 100644 index 0000000..23c45c0 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/boundless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/high/manifest.json b/artifacts/cards/BP-001-moses/high/manifest.json new file mode 100644 index 0000000..8e753b5 --- /dev/null +++ b/artifacts/cards/BP-001-moses/high/manifest.json @@ -0,0 +1,91 @@ +{ + "schemaVersion": 1, + "dimensions": [ + 2000, + 2800 + ], + "files": { + "normal/card.png": { + "sha256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d", + "dimensions": [ + 2000, + 2800 + ] + }, + "normal/card.svg": { + "sha256": "79717d43d87fdcff0fdf99686050718c22e416d0ea536c7627f1bff5e8f98d2a" + }, + "normal/finish-mask.png": { + "sha256": "1ea7ff3ed6eb11ba97c7ff8e50fe7d4d57a30fd264f3dc8e12fb8f241bedc5a9", + "dimensions": [ + 2000, + 2800 + ] + }, + "normal/text-mask.png": { + "sha256": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f", + "dimensions": [ + 2000, + 2800 + ] + }, + "boundless/card.png": { + "sha256": "6eea105658e4c8ca85360b8c0e5225e060085bdc70918b17409728270734ad61", + "dimensions": [ + 2000, + 2800 + ] + }, + "boundless/card.svg": { + "sha256": "253c364cdfdc151801b7b76657695e0d5279edcdc1095b7270fabf4f71a5d1dd" + }, + "boundless/finish-mask.png": { + "sha256": "8ff4c66e561aaaf8d6107fd91680100f345de35744453940be838feae550c8a2", + "dimensions": [ + 2000, + 2800 + ] + }, + "borderless/card.png": { + "sha256": "40ea9c9ae69438164b3bed749f83908f3093b0c8f1ee75ceba9cf78bddc4f13f", + "dimensions": [ + 2000, + 2800 + ] + }, + "borderless/card.svg": { + "sha256": "dca4be38746f79564ce7b6a10c3e34a20b4a6049b148bab11c9c0754b5625d9f" + }, + "borderless/finish-mask.png": { + "sha256": "07c8f84149590e8dbf21b6e51af696d9a9610a291c0bf59508447e9ca7d08994", + "dimensions": [ + 2000, + 2800 + ] + }, + "borderless/text-mask.png": { + "sha256": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f", + "dimensions": [ + 2000, + 2800 + ] + }, + "textless/card.png": { + "sha256": "bb46a64ec206dbc3c7ce1f50790a24deb1292fbba961852a786e4af334118ff9", + "dimensions": [ + 2000, + 2800 + ] + }, + "textless/card.svg": { + "sha256": "4b57537406e51c429999ec6b3cd8d40866b09cd1417d16bf90df8267b3877be9" + }, + "textless/finish-mask.png": { + "sha256": "11a80668e947ea8b58e89764fdbd81b46588ba3fd50a6ca37d7b192128fe37aa", + "dimensions": [ + 2000, + 2800 + ] + } + } +} diff --git a/artifacts/cards/BP-001-moses/high/normal/card.png b/artifacts/cards/BP-001-moses/high/normal/card.png new file mode 100644 index 0000000..841d0eb Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/normal/card.png differ diff --git a/artifacts/cards/BP-001-moses/high/normal/card.svg b/artifacts/cards/BP-001-moses/high/normal/card.svg new file mode 100644 index 0000000..10536e1 --- /dev/null +++ b/artifacts/cards/BP-001-moses/high/normal/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/high/normal/finish-mask.png b/artifacts/cards/BP-001-moses/high/normal/finish-mask.png new file mode 100644 index 0000000..4d62d5a Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/normal/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/high/normal/text-mask.png b/artifacts/cards/BP-001-moses/high/normal/text-mask.png new file mode 100644 index 0000000..196f965 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/normal/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/high/textless/card.png b/artifacts/cards/BP-001-moses/high/textless/card.png new file mode 100644 index 0000000..75d1aad Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/textless/card.png differ diff --git a/artifacts/cards/BP-001-moses/high/textless/card.svg b/artifacts/cards/BP-001-moses/high/textless/card.svg new file mode 100644 index 0000000..c575f32 --- /dev/null +++ b/artifacts/cards/BP-001-moses/high/textless/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/high/textless/finish-mask.png b/artifacts/cards/BP-001-moses/high/textless/finish-mask.png new file mode 100644 index 0000000..a2bcf97 Binary files /dev/null and b/artifacts/cards/BP-001-moses/high/textless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/borderless/card.png b/artifacts/cards/BP-001-moses/low/borderless/card.png new file mode 100644 index 0000000..3f157b9 Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/borderless/card.png differ diff --git a/artifacts/cards/BP-001-moses/low/borderless/finish-mask.png b/artifacts/cards/BP-001-moses/low/borderless/finish-mask.png new file mode 100644 index 0000000..1367047 Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/borderless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/borderless/text-mask.png b/artifacts/cards/BP-001-moses/low/borderless/text-mask.png new file mode 100644 index 0000000..f114322 Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/borderless/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/boundless/card.png b/artifacts/cards/BP-001-moses/low/boundless/card.png new file mode 100644 index 0000000..2463e0c Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/boundless/card.png differ diff --git a/artifacts/cards/BP-001-moses/low/boundless/finish-mask.png b/artifacts/cards/BP-001-moses/low/boundless/finish-mask.png new file mode 100644 index 0000000..cc90b1f Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/boundless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/manifest.json b/artifacts/cards/BP-001-moses/low/manifest.json new file mode 100644 index 0000000..13e9f26 --- /dev/null +++ b/artifacts/cards/BP-001-moses/low/manifest.json @@ -0,0 +1,79 @@ +{ + "schemaVersion": 1, + "dimensions": [ + 500, + 700 + ], + "files": { + "normal/card.png": { + "sha256": "1835792bbd701f051e2128a71cd4ed26a97db64e7e72d3914ef7d97572ffbb31", + "dimensions": [ + 500, + 700 + ] + }, + "normal/finish-mask.png": { + "sha256": "7ef9b993c407d9d37e9477d5b6e8a59cf6f91dbfd951cf1899a31d459c51aa9d", + "dimensions": [ + 500, + 700 + ] + }, + "normal/text-mask.png": { + "sha256": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289", + "dimensions": [ + 500, + 700 + ] + }, + "boundless/card.png": { + "sha256": "fede895433a8eed5f6156504748d47a330ea7a0306d24ab63017dc1c81867c1e", + "dimensions": [ + 500, + 700 + ] + }, + "boundless/finish-mask.png": { + "sha256": "8fb8ba1356a9af345fc97ecd0b1b1b7a645f90d342b974f68b030b402137e9e6", + "dimensions": [ + 500, + 700 + ] + }, + "borderless/card.png": { + "sha256": "84edcdaa24b0f755921500c3ffedc533efedc117004f6d7b6c222e57440f4c21", + "dimensions": [ + 500, + 700 + ] + }, + "borderless/finish-mask.png": { + "sha256": "cfa517b72d82dcaed7bda5c92a5691588a6cf5886a5bb7a4c6e71c9b0065244b", + "dimensions": [ + 500, + 700 + ] + }, + "borderless/text-mask.png": { + "sha256": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289", + "dimensions": [ + 500, + 700 + ] + }, + "textless/card.png": { + "sha256": "b1473c44ea20da61f9937639a70eb6df8ce46984facc4d278e77d9b82f773939", + "dimensions": [ + 500, + 700 + ] + }, + "textless/finish-mask.png": { + "sha256": "eb12314711c2b81dfd1b022087780fb05372fa7166ba164c37fc5b8de6cdbd38", + "dimensions": [ + 500, + 700 + ] + } + } +} diff --git a/artifacts/cards/BP-001-moses/low/normal/card.png b/artifacts/cards/BP-001-moses/low/normal/card.png new file mode 100644 index 0000000..c4afd9c Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/normal/card.png differ diff --git a/artifacts/cards/BP-001-moses/low/normal/finish-mask.png b/artifacts/cards/BP-001-moses/low/normal/finish-mask.png new file mode 100644 index 0000000..8486bbf Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/normal/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/normal/text-mask.png b/artifacts/cards/BP-001-moses/low/normal/text-mask.png new file mode 100644 index 0000000..f114322 Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/normal/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/low/textless/card.png b/artifacts/cards/BP-001-moses/low/textless/card.png new file mode 100644 index 0000000..dde82ee Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/textless/card.png differ diff --git a/artifacts/cards/BP-001-moses/low/textless/finish-mask.png b/artifacts/cards/BP-001-moses/low/textless/finish-mask.png new file mode 100644 index 0000000..4d40f63 Binary files /dev/null and b/artifacts/cards/BP-001-moses/low/textless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/manifest.json b/artifacts/cards/BP-001-moses/manifest.json new file mode 100644 index 0000000..ca07984 --- /dev/null +++ b/artifacts/cards/BP-001-moses/manifest.json @@ -0,0 +1,225 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v05", + "stage": "approved", + "approval": { + "by": "user", + "note": "This looks great. This iteration is approved" + }, + "content": "card.json", + "files": { + "README.md": { + "sha256": "0a1d4aa57e791fab97bf434fccfde8415e22d256d9b13139bb63fee5a636dbab" + }, + "build.py": { + "sha256": "19729b7ba2a42d02788f2a8add6f613a87ed660e1c5dc2cd4944bd6aa1785373" + }, + "card.json": { + "sha256": "f8d257e782d2194c1b0bdcd6adf73c91c2d22876199e6ef2fe8debabac6710a8" + }, + "high/borderless/card.png": { + "sha256": "40ea9c9ae69438164b3bed749f83908f3093b0c8f1ee75ceba9cf78bddc4f13f" + }, + "high/borderless/card.svg": { + "sha256": "dca4be38746f79564ce7b6a10c3e34a20b4a6049b148bab11c9c0754b5625d9f" + }, + "high/borderless/finish-mask.png": { + "sha256": "07c8f84149590e8dbf21b6e51af696d9a9610a291c0bf59508447e9ca7d08994" + }, + "high/borderless/text-mask.png": { + "sha256": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f" + }, + "high/boundless/card.png": { + "sha256": "6eea105658e4c8ca85360b8c0e5225e060085bdc70918b17409728270734ad61" + }, + "high/boundless/card.svg": { + "sha256": "253c364cdfdc151801b7b76657695e0d5279edcdc1095b7270fabf4f71a5d1dd" + }, + "high/boundless/finish-mask.png": { + "sha256": "8ff4c66e561aaaf8d6107fd91680100f345de35744453940be838feae550c8a2" + }, + "high/normal/card.png": { + "sha256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d" + }, + "high/normal/card.svg": { + "sha256": "79717d43d87fdcff0fdf99686050718c22e416d0ea536c7627f1bff5e8f98d2a" + }, + "high/normal/finish-mask.png": { + "sha256": "1ea7ff3ed6eb11ba97c7ff8e50fe7d4d57a30fd264f3dc8e12fb8f241bedc5a9" + }, + "high/normal/text-mask.png": { + "sha256": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f" + }, + "high/textless/card.png": { + "sha256": "bb46a64ec206dbc3c7ce1f50790a24deb1292fbba961852a786e4af334118ff9" + }, + "high/textless/card.svg": { + "sha256": "4b57537406e51c429999ec6b3cd8d40866b09cd1417d16bf90df8267b3877be9" + }, + "high/textless/finish-mask.png": { + "sha256": "11a80668e947ea8b58e89764fdbd81b46588ba3fd50a6ca37d7b192128fe37aa" + }, + "low/borderless/card.png": { + "sha256": "84edcdaa24b0f755921500c3ffedc533efedc117004f6d7b6c222e57440f4c21" + }, + "low/borderless/finish-mask.png": { + "sha256": "cfa517b72d82dcaed7bda5c92a5691588a6cf5886a5bb7a4c6e71c9b0065244b" + }, + "low/borderless/text-mask.png": { + "sha256": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289" + }, + "low/boundless/card.png": { + "sha256": "fede895433a8eed5f6156504748d47a330ea7a0306d24ab63017dc1c81867c1e" + }, + "low/boundless/finish-mask.png": { + "sha256": "8fb8ba1356a9af345fc97ecd0b1b1b7a645f90d342b974f68b030b402137e9e6" + }, + "low/normal/card.png": { + "sha256": "1835792bbd701f051e2128a71cd4ed26a97db64e7e72d3914ef7d97572ffbb31" + }, + "low/normal/finish-mask.png": { + "sha256": "7ef9b993c407d9d37e9477d5b6e8a59cf6f91dbfd951cf1899a31d459c51aa9d" + }, + "low/normal/text-mask.png": { + "sha256": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289" + }, + "low/textless/card.png": { + "sha256": "b1473c44ea20da61f9937639a70eb6df8ce46984facc4d278e77d9b82f773939" + }, + "low/textless/finish-mask.png": { + "sha256": "eb12314711c2b81dfd1b022087780fb05372fa7166ba164c37fc5b8de6cdbd38" + }, + "med/borderless/card.png": { + "sha256": "88726c0c6326df666df07c13c1d88aac314f4454c7cceb866797709d8c9f052a" + }, + "med/borderless/finish-mask.png": { + "sha256": "ee75326152bfe25ee4abc7cc1c9f2623df0ad92ce524f6866c8c088007ea5739" + }, + "med/borderless/text-mask.png": { + "sha256": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6" + }, + "med/boundless/card.png": { + "sha256": "8ff29b5cff2d8ad37f16925f3d0bc6e2102e5f8bb5eaca1401c43e30e58d5508" + }, + "med/boundless/finish-mask.png": { + "sha256": "1a93b38af6fbbaf8535a48e4f3ee0eb19554edec8f5fac80fc4db8974fb9a4ef" + }, + "med/normal/card.png": { + "sha256": "c7ae7af2c377920c734d2dbbb2f933f3e30720877c7c42f9dcfd5874dfaeff45" + }, + "med/normal/finish-mask.png": { + "sha256": "bf66bfbd3a66ccde7ec565fc0dc6997826098f8490c74fb930fb100752f119ba" + }, + "med/normal/text-mask.png": { + "sha256": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6" + }, + "med/textless/card.png": { + "sha256": "403b7ed0b163709747a32d62baa9bd50281fc0ea2b72996fa18b2abee6f4d98d" + }, + "med/textless/finish-mask.png": { + "sha256": "e51b00c6812a7fa7488f0e2c733f50857562080e269f018ffb4c6501db6c849c" + }, + "source/art-master.png": { + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee" + }, + "source/base-v04.png": { + "sha256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e" + }, + "source/build-art.py": { + "sha256": "dba1118774024b05715dfc84a81db80c9a1f872d3d2a4871435cfb9b99006fd4" + }, + "source/foreground/cloud-crown-over-frame-2000.png": { + "sha256": "c9f3f393b7fe9b99e9106bd1a8b95bd49211b9905cd67df526e3aba3c7340bc2" + }, + "source/foreground/curtain-over-rail-2000.png": { + "sha256": "3c9f80ec1c9e800d648614f25eec5c5685c823313eab03c734f885a3e57b899b" + }, + "source/foreground/foreground-2000.png": { + "sha256": "094ec94400a21dd78f8e237104ebf11bf8cafc868f27c1ef78b640b7b3d5f857" + }, + "source/foreground/selected-foreground.svg": { + "sha256": "3a9891150ba6f147d001732754a8813b180f04f232efe08eac7f7df8f4c3bdc1" + }, + "source/foreground/textless-frame-occlusion-2000.png": { + "sha256": "d7287dc16083a32f7591ede2d50497063a3a53096e108d9ac3420ea2e8e5e0ab" + }, + "source/layers/backing-2000.png": { + "sha256": "89478ebd88e5566e0cf4638afa5169ea96ec710923ea19328d988b89aee8df99" + }, + "source/layers/backing-borderless-2000.png": { + "sha256": "253378ecbe1382e1bb9a58cc1fc843c111851c5981dd725594f8327e0eaabdd7" + }, + "source/layers/backing.svg": { + "sha256": "b7c992b7c787a74887344ee9adb41de8787130f0c87d57cd06c371d40857df61" + }, + "source/layers/frame-2000.png": { + "sha256": "378c7ce90ab09752c70c0a9aae6d59f589d3a5de29824563e66230b1e32a6779" + }, + "source/layers/frame-textless-2000.png": { + "sha256": "c4769f979af12c625fe647428a8c163e5eb76f8738275beccbeb7667b44aadf3" + }, + "source/layers/frame.svg": { + "sha256": "219f5e30ed79e66e523380d7b82e731513962bfef5fd7d8db8d9c10b89a75128" + }, + "source/layers/layout.json": { + "sha256": "b77390939d86148c1c8d998cc959f307aa87f8fcfa14cd38a252d814d1e64c26" + }, + "source/layers/overlay-normal-2000.png": { + "sha256": "bee69abed9d8a84396823cfd40e2cfbd23e7b562a3317d628fd32f94bd0dd97d" + }, + "source/layers/selected-overlay.svg": { + "sha256": "c2ca725ac8821a62d8af8176cd25ecf5e4a9c317dc62947cc8242a77c8f53e00" + }, + "source/material/base-pane-coverage.png": { + "sha256": "0cf788e72fcddbc7b599b167aaf5da6f0358901c256a9da2ccc6294f52e9e2cc" + }, + "source/material/base-raw-ridges.png": { + "sha256": "49a5f558de7fa0c132f3d8fda00a3708fb244a4f89556d13b34c34b45efbc3e1" + }, + "source/material/foreground-analysis-plate.png": { + "sha256": "1eff6ea4fdb0b43f0a658e4feb46729988c85efa4fd37e833ed24db7a8b31fec" + }, + "source/material/foreground-pane-coverage.png": { + "sha256": "b7eb159ef60c982b60c5e75750b59b143da31022466f11a1ff017548298bfee5" + }, + "source/material/foreground-raw-ridges.png": { + "sha256": "af2ca47e623f33d4ac854fea9740fdf638403fd2c4e3c3146dd1fa92c429b467" + }, + "source/post-extension.png": { + "sha256": "0f3e3995b230b807a7f1d64076dce5505010499c9cb2574431d3bf187de6b3e6" + }, + "source/prior-revision-hashes.json": { + "sha256": "694e27b8451cc42ff0545c9609a943aaf1735d756b7f75922be7f4892f72a881" + }, + "source/production-inputs.json": { + "sha256": "c80050ce85e9ca4f0cb7f3e59d8efa5c02efbe6f1033d8469f0c088d6acb4b72" + }, + "source/provenance.json": { + "sha256": "550f9d1194339bec62d346acd9a34bdc31439f72e004545f4e130d504fae9946" + }, + "source/selected-normal-2000.png": { + "sha256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d" + }, + "source/text/selected-text.svg": { + "sha256": "57c51e06cfb8732f1500311936f95b4f4e0255af35b0218341f1126ce8370741" + }, + "source/text/text-2000.png": { + "sha256": "2623f00ffe8d9e5883215e08452f2ecd51eb21bb55ab79f485ce6ca82f1e1155" + }, + "source/typography-layout.json": { + "sha256": "12eafec1307d5292534d7cc92ac426fef5f4fa0977d2043fc4a851b83332cad2" + }, + "low/manifest.json": { + "sha256": "d401401d294a6e262aa5fce84ddb6068d20cdbc988076c3ce07f1fe45b15c7be" + }, + "med/manifest.json": { + "sha256": "45463c5de93d075189980c1c6c50dc9fd93302af35549e783c23aa9f13f72806" + }, + "high/manifest.json": { + "sha256": "6373b422e0a7a670a471019f91de4d1b86786bf82bf63433f5fd3e3f778692ab" + } + }, + "compatibilityAliases": [] +} diff --git a/artifacts/cards/BP-001-moses/med/borderless/card.png b/artifacts/cards/BP-001-moses/med/borderless/card.png new file mode 100644 index 0000000..0391a4f Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/borderless/card.png differ diff --git a/artifacts/cards/BP-001-moses/med/borderless/finish-mask.png b/artifacts/cards/BP-001-moses/med/borderless/finish-mask.png new file mode 100644 index 0000000..236aa62 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/borderless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/med/borderless/text-mask.png b/artifacts/cards/BP-001-moses/med/borderless/text-mask.png new file mode 100644 index 0000000..1e3e7a6 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/borderless/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/med/boundless/card.png b/artifacts/cards/BP-001-moses/med/boundless/card.png new file mode 100644 index 0000000..9d9e858 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/boundless/card.png differ diff --git a/artifacts/cards/BP-001-moses/med/boundless/finish-mask.png b/artifacts/cards/BP-001-moses/med/boundless/finish-mask.png new file mode 100644 index 0000000..9443ce8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/boundless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/med/manifest.json b/artifacts/cards/BP-001-moses/med/manifest.json new file mode 100644 index 0000000..953ec1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/med/manifest.json @@ -0,0 +1,79 @@ +{ + "schemaVersion": 1, + "dimensions": [ + 1000, + 1400 + ], + "files": { + "normal/card.png": { + "sha256": "c7ae7af2c377920c734d2dbbb2f933f3e30720877c7c42f9dcfd5874dfaeff45", + "dimensions": [ + 1000, + 1400 + ] + }, + "normal/finish-mask.png": { + "sha256": "bf66bfbd3a66ccde7ec565fc0dc6997826098f8490c74fb930fb100752f119ba", + "dimensions": [ + 1000, + 1400 + ] + }, + "normal/text-mask.png": { + "sha256": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6", + "dimensions": [ + 1000, + 1400 + ] + }, + "boundless/card.png": { + "sha256": "8ff29b5cff2d8ad37f16925f3d0bc6e2102e5f8bb5eaca1401c43e30e58d5508", + "dimensions": [ + 1000, + 1400 + ] + }, + "boundless/finish-mask.png": { + "sha256": "1a93b38af6fbbaf8535a48e4f3ee0eb19554edec8f5fac80fc4db8974fb9a4ef", + "dimensions": [ + 1000, + 1400 + ] + }, + "borderless/card.png": { + "sha256": "88726c0c6326df666df07c13c1d88aac314f4454c7cceb866797709d8c9f052a", + "dimensions": [ + 1000, + 1400 + ] + }, + "borderless/finish-mask.png": { + "sha256": "ee75326152bfe25ee4abc7cc1c9f2623df0ad92ce524f6866c8c088007ea5739", + "dimensions": [ + 1000, + 1400 + ] + }, + "borderless/text-mask.png": { + "sha256": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6", + "dimensions": [ + 1000, + 1400 + ] + }, + "textless/card.png": { + "sha256": "403b7ed0b163709747a32d62baa9bd50281fc0ea2b72996fa18b2abee6f4d98d", + "dimensions": [ + 1000, + 1400 + ] + }, + "textless/finish-mask.png": { + "sha256": "e51b00c6812a7fa7488f0e2c733f50857562080e269f018ffb4c6501db6c849c", + "dimensions": [ + 1000, + 1400 + ] + } + } +} diff --git a/artifacts/cards/BP-001-moses/med/normal/card.png b/artifacts/cards/BP-001-moses/med/normal/card.png new file mode 100644 index 0000000..8508f81 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/normal/card.png differ diff --git a/artifacts/cards/BP-001-moses/med/normal/finish-mask.png b/artifacts/cards/BP-001-moses/med/normal/finish-mask.png new file mode 100644 index 0000000..1ec02dd Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/normal/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/med/normal/text-mask.png b/artifacts/cards/BP-001-moses/med/normal/text-mask.png new file mode 100644 index 0000000..1e3e7a6 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/normal/text-mask.png differ diff --git a/artifacts/cards/BP-001-moses/med/textless/card.png b/artifacts/cards/BP-001-moses/med/textless/card.png new file mode 100644 index 0000000..97f3187 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/textless/card.png differ diff --git a/artifacts/cards/BP-001-moses/med/textless/finish-mask.png b/artifacts/cards/BP-001-moses/med/textless/finish-mask.png new file mode 100644 index 0000000..f7db876 Binary files /dev/null and b/artifacts/cards/BP-001-moses/med/textless/finish-mask.png differ diff --git a/artifacts/cards/BP-001-moses/review/art-card-size.png b/artifacts/cards/BP-001-moses/review/art-card-size.png new file mode 100644 index 0000000..f9912cb Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/art-card-size.png differ diff --git a/artifacts/cards/BP-001-moses/review/art-review.png b/artifacts/cards/BP-001-moses/review/art-review.png new file mode 100644 index 0000000..f6f9bb9 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/art-review.png differ diff --git a/artifacts/cards/BP-001-moses/review/art-thumbnail.png b/artifacts/cards/BP-001-moses/review/art-thumbnail.png new file mode 100644 index 0000000..3f07c46 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/art-thumbnail.png differ diff --git a/artifacts/cards/BP-001-moses/review/build-validation.json b/artifacts/cards/BP-001-moses/review/build-validation.json new file mode 100644 index 0000000..0cf5472 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/build-validation.json @@ -0,0 +1,463 @@ +{ + "status": "passed", + "cardId": "BP-001", + "revision": "v05", + "finalCardApproval": null, + "assemblyAuthorization": "Okay. Let's finish the Moses card with the actual generating the prints/masks", + "selectedPrototype": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7", + "normalSHA256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d", + "validationSHA256": "e5fe7a009d03ecc33ee6a92b1c357f78992b39e88e58c51079eb5675546c7772", + "authority": "v7 comparison PNG; inherited antialias pixels intentionally pinned by the selected proof" + }, + "inputs": { + "art": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee" + }, + "foreground": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/foreground/foreground-2000.png", + "sha256": "094ec94400a21dd78f8e237104ebf11bf8cafc868f27c1ef78b640b7b3d5f857" + }, + "text": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/text/text-2000.png", + "sha256": "2623f00ffe8d9e5883215e08452f2ecd51eb21bb55ab79f485ce6ca82f1e1155" + }, + "builderSHA256": "19729b7ba2a42d02788f2a8add6f613a87ed660e1c5dc2cd4944bd6aa1785373" + }, + "bespokeLayers": { + "template": "BP-001-moses-legendary-tent-textile-v1", + "canvas": [ + 2000, + 2800 + ], + "compositionOrder": [ + "base-art", + "frame", + "curtain-over-rail", + "backing", + "adapted-cloud-and-sleeve", + "text" + ], + "printingComponents": { + "normal": [ + "base-art", + "frame", + "curtain-over-rail", + "backing", + "adapted-foreground", + "text" + ], + "boundless": [ + "base-art" + ], + "borderless": [ + "base-art", + "backing", + "adapted-foreground", + "text" + ], + "textless": [ + "base-art", + "frame", + "curtain-over-rail", + "cloud-crown-over-frame" + ] + } + }, + "typography": { + "sharedFontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "title": { + "font": "P052-Bold.otf", + "weight": 700, + "size": 142 + }, + "verse": { + "font": "SanctificationP052-Medium.otf", + "weight": 500, + "size": 66, + "lines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ] + }, + "reference": { + "font": "SanctificationP052-Medium.otf", + "weight": 500, + "size": 48, + "text": "Deuteronomy 34:10 \u2022 ESV" + }, + "proofChecks": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen", + "baseline": 2189, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 172, + 2140, + 889, + 2191 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "a prophet since in Israel", + "baseline": 2299, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 185, + 2250, + 876, + 2318 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "like Moses, whom the LORD", + "baseline": 2409, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 112, + 2360, + 948, + 2420 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "knew face to face", + "baseline": 2519, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 282, + 2470, + 778, + 2521 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2640, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 253, + 2605, + 807, + 2654 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 112, + 2140, + 948, + 2521 + ], + "upperFlourishToVerseInk": 84, + "verseInkToReferenceLineBox": 84, + "centeringDifference": 0, + "lineBaselineGap": 110, + "requestedSemanticLines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ], + "usableBandTop": 2056 + } + }, + "textMask": { + "expansionRadius": 12, + "gaussianRadius": 8, + "units": "master pixels" + }, + "finishRecipe": { + "id": "raw-ridges-v1", + "sha256": "23331ce56a717edb4e6f85956d11fdd98397009c4e6dd230d8c334c77571dd18", + "path": "tools/card-production/recipes/raw-ridges-v1.json", + "baseAnalysisCount": 1, + "adaptedForegroundAnalysisCount": 1 + }, + "normalAuthorityCheck": { + "productionEqualsSelectedPrototype": true, + "selectedSHA256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d", + "productionSHA256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d", + "reconstructionChangedPixels": 3990925, + "reconstructionMaximumChannelDelta": 127 + }, + "printings": { + "normal": { + "components": [ + "base-art", + "frame", + "curtain-over-rail", + "backing", + "adapted-foreground", + "text" + ], + "textMask": true, + "finishCoverage": "base plus depth-resolved adapted foreground", + "exports": [ + { + "resolution": "low", + "dimensions": [ + 500, + 700 + ], + "files": { + "card.png": "1835792bbd701f051e2128a71cd4ed26a97db64e7e72d3914ef7d97572ffbb31", + "finish-mask.png": "7ef9b993c407d9d37e9477d5b6e8a59cf6f91dbfd951cf1899a31d459c51aa9d", + "text-mask.png": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289" + } + }, + { + "resolution": "med", + "dimensions": [ + 1000, + 1400 + ], + "files": { + "card.png": "c7ae7af2c377920c734d2dbbb2f933f3e30720877c7c42f9dcfd5874dfaeff45", + "finish-mask.png": "bf66bfbd3a66ccde7ec565fc0dc6997826098f8490c74fb930fb100752f119ba", + "text-mask.png": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6" + } + }, + { + "resolution": "high", + "dimensions": [ + 2000, + 2800 + ], + "files": { + "card.png": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d", + "finish-mask.png": "1ea7ff3ed6eb11ba97c7ff8e50fe7d4d57a30fd264f3dc8e12fb8f241bedc5a9", + "text-mask.png": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f" + } + } + ] + }, + "boundless": { + "components": [ + "base-art" + ], + "textMask": false, + "finishCoverage": "base illustration", + "exports": [ + { + "resolution": "low", + "dimensions": [ + 500, + 700 + ], + "files": { + "card.png": "fede895433a8eed5f6156504748d47a330ea7a0306d24ab63017dc1c81867c1e", + "finish-mask.png": "8fb8ba1356a9af345fc97ecd0b1b1b7a645f90d342b974f68b030b402137e9e6" + } + }, + { + "resolution": "med", + "dimensions": [ + 1000, + 1400 + ], + "files": { + "card.png": "8ff29b5cff2d8ad37f16925f3d0bc6e2102e5f8bb5eaca1401c43e30e58d5508", + "finish-mask.png": "1a93b38af6fbbaf8535a48e4f3ee0eb19554edec8f5fac80fc4db8974fb9a4ef" + } + }, + { + "resolution": "high", + "dimensions": [ + 2000, + 2800 + ], + "files": { + "card.png": "6eea105658e4c8ca85360b8c0e5225e060085bdc70918b17409728270734ad61", + "finish-mask.png": "8ff4c66e561aaaf8d6107fd91680100f345de35744453940be838feae550c8a2" + } + } + ] + }, + "borderless": { + "components": [ + "base-art", + "backing", + "adapted-foreground", + "text" + ], + "textMask": true, + "finishCoverage": "base plus depth-resolved adapted foreground", + "exports": [ + { + "resolution": "low", + "dimensions": [ + 500, + 700 + ], + "files": { + "card.png": "84edcdaa24b0f755921500c3ffedc533efedc117004f6d7b6c222e57440f4c21", + "finish-mask.png": "cfa517b72d82dcaed7bda5c92a5691588a6cf5886a5bb7a4c6e71c9b0065244b", + "text-mask.png": "bd3e477df1354f2df81cca9585913502ba4d2f4f68b26c81b42a0d907da0e289" + } + }, + { + "resolution": "med", + "dimensions": [ + 1000, + 1400 + ], + "files": { + "card.png": "88726c0c6326df666df07c13c1d88aac314f4454c7cceb866797709d8c9f052a", + "finish-mask.png": "ee75326152bfe25ee4abc7cc1c9f2623df0ad92ce524f6866c8c088007ea5739", + "text-mask.png": "43a26baa3d9155f8bb711f05b3e589f0cef757e0561b109dc01d5e7ab0681be6" + } + }, + { + "resolution": "high", + "dimensions": [ + 2000, + 2800 + ], + "files": { + "card.png": "40ea9c9ae69438164b3bed749f83908f3093b0c8f1ee75ceba9cf78bddc4f13f", + "finish-mask.png": "07c8f84149590e8dbf21b6e51af696d9a9610a291c0bf59508447e9ca7d08994", + "text-mask.png": "c75a0c9e0250707b0e498307d0793351a5b27a162732986336c4038c86a9327f" + } + } + ] + }, + "textless": { + "components": [ + "base-art", + "frame", + "curtain-over-rail", + "cloud-crown-over-frame" + ], + "textMask": false, + "finishCoverage": "base illustration with frame depth resolved", + "exports": [ + { + "resolution": "low", + "dimensions": [ + 500, + 700 + ], + "files": { + "card.png": "b1473c44ea20da61f9937639a70eb6df8ce46984facc4d278e77d9b82f773939", + "finish-mask.png": "eb12314711c2b81dfd1b022087780fb05372fa7166ba164c37fc5b8de6cdbd38" + } + }, + { + "resolution": "med", + "dimensions": [ + 1000, + 1400 + ], + "files": { + "card.png": "403b7ed0b163709747a32d62baa9bd50281fc0ea2b72996fa18b2abee6f4d98d", + "finish-mask.png": "e51b00c6812a7fa7488f0e2c733f50857562080e269f018ffb4c6501db6c849c" + } + }, + { + "resolution": "high", + "dimensions": [ + 2000, + 2800 + ], + "files": { + "card.png": "bb46a64ec206dbc3c7ce1f50790a24deb1292fbba961852a786e4af334118ff9", + "finish-mask.png": "11a80668e947ea8b58e89764fdbd81b46588ba3fd50a6ca37d7b192128fe37aa" + } + } + ] + } + }, + "tools": { + "inkscape": "Inkscape 1.2.2 (b0a8486541, 2022-12-01)", + "pillow": "10.2.0", + "numpy": "1.26.4" + }, + "visualReview": { + "static": "pending user review", + "movingLight": "not performed", + "userApproval": "pending" + } +} diff --git a/artifacts/cards/BP-001-moses/review/finish-masks-comparison.png b/artifacts/cards/BP-001-moses/review/finish-masks-comparison.png new file mode 100644 index 0000000..29fee0a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/finish-masks-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/fonts.conf b/artifacts/cards/BP-001-moses/review/fonts.conf new file mode 100644 index 0000000..35f9726 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/illustration-review.md b/artifacts/cards/BP-001-moses/review/illustration-review.md new file mode 100644 index 0000000..1ff7acb --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/illustration-review.md @@ -0,0 +1,9 @@ +# Moses v05 — continuous near entrance post + +Inspected at card size and enlarged upper/ground joins. The previously interrupted gold post now connects through the cloud to the ground socket, restoring the near entrance structure. Its width and slight leftward descent follow the upper post and counterpart support. Dark structural leading, unequal panel lengths, ivory highlight facets and restrained sampled glass variation connect its design to the existing posts. No extra textile binding was added. + +The correction remains visually simple and narrow. At enlarged size its authored segments are more regular and cleaner than generated surrounding glass; the join and endpoint are retained for explicit review in `post-join-detail.png`. At card size it reads as one continuous support. No broad generative repaint was used. + +Pixel comparison proves the entire remaining image is untouched: 72,357 changed pixels, exclusive bounding box `[318,994,377,2647]`, 1.2921% of the 2000×2800 canvas. All pixels outside the authorized post rectangle are identical to v04. Rebuilding produced byte-identical output images. Prior v04 files were hash-checked unchanged. + +This is art-only, pending user review. No card frame, backing, text, printing, masks or final approval were created. The transparent post source is a narrow illustration correction, not a production printing layer package. diff --git a/artifacts/cards/BP-001-moses/review/layers-comparison.png b/artifacts/cards/BP-001-moses/review/layers-comparison.png new file mode 100644 index 0000000..85345ea Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/layers-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/README.md b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/README.md new file mode 100644 index 0000000..96e9d7c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/README.md @@ -0,0 +1,30 @@ +# Moses v05 — Normal prototype + +Review-only composition using the selected v05 art. The liked nameplate geometry, P052 typography and suspended linen layout carry forward from v03 layout-preview-astra-v5. New wave-like contours follow the pearl/cool cloud, replacing the previous round lobes. The lower closure is authored above the original ground occlusion. The existing continuous near post is retained from source pixels; no second post is drawn. + +- [Full preview](composed-preview-1000.png) +- [500 px preview](composed-preview-500.png) +- [Source/composed comparison](source-composed-comparison.png) +- [Prior/current layout comparison](layout-lineage-comparison.png) +- [Enlarged junction details](cloud-textile-junction-details.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The linen field has a shallow sag, curved left edge, woven hem and right frame ties. Its left corner extends behind the cloud toward the existing post. The fine gold enclosure supports the illustrated curtain and cloud. The lineage comparison intentionally includes different source illustrations. + +Source: ../../source/art-master.png, unchanged at original 2000 × 2800 registration. SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. + +Exact title: `MOSES` + +Exact narrator text, without quotation marks or added final punctuation: + +`And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face` + +Exact reference: `Deuteronomy 34:10 • ESV` + +Validation checks exact content, P052 font hashes/glyph coverage, measured safe bounds, opaque backing beneath all glyphs, zero foreground/text intersection, unchanged source hash and text-pixel equality to the carried-forward layout. Verse spacing is 66 master pixels above and 71 pixels to reference ink. Geometry renders at 2× with premultiplied-alpha Lanczos downsampling; glass is not blurred. Static review includes full card and enlarged junctions. + +Rebuild from the repository: python3 in-progress/cards/BP-001-moses/revisions/v05/review/normal-prototype-v1/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Normal prototype only. No Borderless, Textless, Boundless, finish masks, production glyph masks, harness fixture, acceptance or promotion. Earlier revisions and candidates are preserved. diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.png new file mode 100644 index 0000000..48f17ca Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.svg new file mode 100644 index 0000000..91b2c13 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/build-preview.py b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/build-preview.py new file mode 100644 index 0000000..aae5b72 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/build-preview.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z' +title_inner='' +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +# Ties sit at the existing right rail and visibly support the hanging field. +ties=''+''.join(f'' for y in [2110,2700])+'' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='M724 2080 C720 2160 692 2180 704 2230 C711 2265 694 2294 677 2322 C664 2345 672 2385 695 2410 C721 2440 722 2498 678 2538 C642 2543 622 2480 586 2480 C535 2480 536 2458 500 2444 C462 2421 438 2445 409 2424 C384 2408 370 2375 370 2340' +cloud_top=top_edge+' L780 490 H0 V0Z' +cloud_low='M0 400H370 V1780H765 C765 1920 738 2010 724 2080 '+lower_edge.removeprefix('M724 2080 ')+' L369 2640 Q286 2659 205 2635 Q101 2583 0 2612Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top','cloud-low'])+lead+'' +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen a',1320,2360,74,'Sanctification P052',500), + ('verse-1','prophet since in Israel like Moses,',1320,2468,74,'Sanctification P052',500), + ('verse-2','whom the LORD knew face to face',1320,2576,74,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2690,57,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [730,2275,1900,2730] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2240;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=20,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only prototype — pearl cloud and suspended tent textile', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':108, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Liked nameplate geometry and suspended linen layout retained. New v05 pearl/cool cloud uses authored wave-like cubic contours; lower closure stays above source ground occlusion. The continuous entrance post is reused from the baked source and never redrawn.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Source/composed review and comparison to the carried-forward layout. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=REPO/'in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('v05 Normal prototype',P/'composed-preview-1000.png')]), + ('layout-lineage-comparison',[('Prior v03 layout / prior art',previous/'composed-preview-1000.png'),('v05 layout / current art',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1500,1300),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Pearl cloud / nameplate',(620,0,1140,480),(22,65),(700,550)), + ('Cloud / textile / existing post',(295,2020,800,2660),(795,65),(660,690)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((round(im.width*1.15),round(im.height*1.15)),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +im=master.crop((290,2070,2000,2800));im.thumbnail((1450,520));detail.paste(im,(25,775)) +detail.save(P/'cloud-textile-junction-details.png') +# Preserve approved typography and nameplate geometry without claiming the +# changed source/cloud pixels are identical to old artwork. +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert np.array_equal(np.asarray(Image.open(P/'text-preview.png')),np.asarray(Image.open(previous/'text-preview.png'))) +report['structuralChecks']['textPixelsIdenticalToCarriedForwardLayout']=True +report['structuralChecks']['sourceMatchesV05CardSHA256']=True +report['structuralChecks']['secondPostDrawn']=False +report['template']['layoutReference']=str(previous.relative_to(REPO)) +report['template']['postPolicy']='Use the continuous entrance post already baked into v05 artwork; no new post layer' +report['template']['leftTextileAnchor']=[340,2106] +report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/cloud-textile-junction-details.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/cloud-textile-junction-details.png new file mode 100644 index 0000000..cf9455e Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/cloud-textile-junction-details.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-1000.png new file mode 100644 index 0000000..59b000c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-2000.png new file mode 100644 index 0000000..c5ff409 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-500.png new file mode 100644 index 0000000..c5dedcb Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview.svg new file mode 100644 index 0000000..6e5f5d7 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-comparison.png new file mode 100644 index 0000000..f44b1f9 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-junction-detail.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-junction-detail.png new file mode 100644 index 0000000..223af97 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/curtain-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/fonts.conf b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/fonts.conf new file mode 100644 index 0000000..04073a5 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/normal-prototype-v1/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.png new file mode 100644 index 0000000..7956829 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.svg new file mode 100644 index 0000000..f1de0b2 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/layout-lineage-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/layout-lineage-comparison.png new file mode 100644 index 0000000..5e847a8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/layout-lineage-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/preview-validation.json b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/preview-validation.json new file mode 100644 index 0000000..51314f6 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/preview-validation.json @@ -0,0 +1,193 @@ +{ + "status": "passed", + "scope": "v05 Normal-only prototype \u2014 pearl cloud and suspended tent textile", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "M0 400H370 V1780H765 C765 1920 738 2010 724 2080 C720 2160 692 2180 704 2230 C711 2265 694 2294 677 2322 C664 2345 672 2385 695 2410 C721 2440 722 2498 678 2538 C642 2543 622 2480 586 2480 C535 2480 536 2458 500 2444 C462 2421 438 2445 409 2424 C384 2408 370 2375 370 2340 L369 2640 Q286 2659 205 2635 Q101 2583 0 2612Z", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 2651 + ], + "curtainFrameOrBackingOverlapPixels": 216058, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5", + "postPolicy": "Use the continuous entrance post already baked into v05 artwork; no new post layer", + "leftTextileAnchor": [ + 340, + 2106 + ] + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a", + "baseline": 2360, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 890, + 2306, + 1749, + 2362 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "prophet since in Israel like Moses,", + "baseline": 2468, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 769, + 2414, + 1869, + 2490 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "whom the LORD knew face to face", + "baseline": 2576, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 752, + 2521, + 1886, + 2578 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2690, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2649, + 1649, + 2707 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 752, + 2306, + 1886, + 2578 + ], + "upperFlourishToVerseInk": 66, + "verseInkToReferenceLineBox": 71, + "centeringDifference": 5, + "lineBaselineGap": 108 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "textPixelsIdenticalToCarriedForwardLayout": true, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false, + "alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo" + }, + "foregroundAdaptation": "Liked nameplate geometry and suspended linen layout retained. New v05 pearl/cool cloud uses authored wave-like cubic contours; lower closure stays above source ground occlusion. The continuous entrance post is reused from the baked source and never redrawn." +} diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.png new file mode 100644 index 0000000..6d159c1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.svg new file mode 100644 index 0000000..02643e7 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/source-composed-comparison.png new file mode 100644 index 0000000..7622b18 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.png new file mode 100644 index 0000000..11508e0 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.svg new file mode 100644 index 0000000..789c094 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.png new file mode 100644 index 0000000..ea7151c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.svg new file mode 100644 index 0000000..f7f4d81 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not arisen a \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.png new file mode 100644 index 0000000..ac2914f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.svg new file mode 100644 index 0000000..cd91462 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-1-ink.svg @@ -0,0 +1 @@ +prophet since in Israel like Moses, \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.png new file mode 100644 index 0000000..c84272f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.svg new file mode 100644 index 0000000..bad6927 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/verse-2-ink.svg @@ -0,0 +1 @@ +whom the LORD knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-1000.png new file mode 100644 index 0000000..14607f7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-2000.png new file mode 100644 index 0000000..d97b5b1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-500.png new file mode 100644 index 0000000..7524bd3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain.svg new file mode 100644 index 0000000..02635db --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v1/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/README.md b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/README.md new file mode 100644 index 0000000..4c43008 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/README.md @@ -0,0 +1,24 @@ +# Moses v05 — Normal prototype v2 + +Targeted refinement of [v1](../normal-prototype-v1/README.md), which remains intact. + +- [Full preview](composed-preview-1000.png) +- [500 px preview](composed-preview-500.png) +- [V1 / V2 full comparison](v1-v2-full-comparison.png) +- [V1 / V2 lower junction comparison](v1-v2-junction-comparison.png) +- [Enlarged cloud/textile details](cloud-textile-junction-details.png) +- [Source/composed comparison](source-composed-comparison.png) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The foreground now follows the actual curtain hem through its full lower edge, hiding the frame behind the fabric. The old shortened extraction exposed a gold frame rule inside the hem. A dedicated check verifies zero exposed frame pixels within the affected cloth region. + +The lowest cloud overflow is now a broader rounded roll. Continuous cubic tangents replace the previous pointed closure; its pearl and cool glass interior remains registered to the source. No blur, new post or source repaint is introduced. + +All text and backing pixels match v1 exactly. The upper composition, including the liked nameplate and cloud crown, is preserved. Checks cover exact text, font hashes/glyph coverage, safe glyph bounds, opaque backing, zero foreground/text intersection, source SHA256 and targeted v1/v2 preservation. Rendering uses 2× resolution and premultiplied-alpha downsampling. + +Source: ../../source/art-master.png, unchanged SHA256 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. The continuous entrance post is already in this source and is not drawn again. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/normal-prototype-v2/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Normal review prototype only. No other printing types, finish masks, production exports, harness fixture, acceptance or promotion. diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.png new file mode 100644 index 0000000..48f17ca Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.svg new file mode 100644 index 0000000..c340c85 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/build-preview.py b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/build-preview.py new file mode 100644 index 0000000..2e23b85 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/build-preview.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z' +title_inner='' +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +# Ties sit at the existing right rail and visibly support the hanging field. +ties=''+''.join(f'' for y in [2110,2700])+'' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='M724 2080 C720 2160 692 2180 704 2230 C711 2265 694 2294 677 2322 C664 2345 672 2385 695 2410 C721 2440 722 2478 701 2501 C680 2524 647 2527 619 2509 C594 2492 566 2496 537 2485 C510 2474 499 2459 471 2455 C436 2450 413 2442 393 2423 C377 2408 370 2375 370 2340' +cloud_top=top_edge+' L780 490 H0 V0Z' +cloud_low='M0 400H370 V1780H765 C765 1920 738 2010 724 2080 '+lower_edge.removeprefix('M724 2080 ')+' L369 2650 C318 2651 271 2644 224 2641 C173 2638 122 2650 76 2646 C45 2640 24 2634 0 2640Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top','cloud-low'])+lead+'' +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen a',1320,2360,74,'Sanctification P052',500), + ('verse-1','prophet since in Israel like Moses,',1320,2468,74,'Sanctification P052',500), + ('verse-2','whom the LORD knew face to face',1320,2576,74,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2690,57,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [730,2275,1900,2730] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2240;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=20,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only prototype — pearl cloud and suspended tent textile', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':108, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Liked nameplate geometry and suspended linen layout retained. New v05 pearl/cool cloud uses authored wave-like cubic contours; lower closure stays above source ground occlusion. The continuous entrance post is reused from the baked source and never redrawn.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Source/composed review and comparison to the carried-forward layout. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=REPO/'in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('v05 Normal prototype',P/'composed-preview-1000.png')]), + ('layout-lineage-comparison',[('Prior v03 layout / prior art',previous/'composed-preview-1000.png'),('v05 layout / current art',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1500,1300),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Pearl cloud / nameplate',(620,0,1140,480),(22,65),(700,550)), + ('Cloud / textile / existing post',(295,2020,800,2660),(795,65),(660,690)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((round(im.width*1.15),round(im.height*1.15)),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +im=master.crop((290,2070,2000,2800));im.thumbnail((1450,520));detail.paste(im,(25,775)) +detail.save(P/'cloud-textile-junction-details.png') +# Preserve approved typography and nameplate geometry without claiming the +# changed source/cloud pixels are identical to old artwork. +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert np.array_equal(np.asarray(Image.open(P/'text-preview.png')),np.asarray(Image.open(previous/'text-preview.png'))) +report['structuralChecks']['textPixelsIdenticalToCarriedForwardLayout']=True +report['structuralChecks']['sourceMatchesV05CardSHA256']=True +report['structuralChecks']['secondPostDrawn']=False +report['template']['layoutReference']=str(previous.relative_to(REPO)) +report['template']['postPolicy']='Use the continuous entrance post already baked into v05 artwork; no new post layer' +report['template']['leftTextileAnchor']=[340,2106] +report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') + +# Targeted v1/v2 regression checks and comparison assets. +v1=P.parent/'normal-prototype-v1' +assert np.array_equal(np.asarray(Image.open(P/'text-preview.png')),np.asarray(Image.open(v1/'text-preview.png'))) +assert np.array_equal(np.asarray(Image.open(P/'border-backing-preview.png')),np.asarray(Image.open(v1/'border-backing-preview.png'))) +current=np.asarray(Image.open(P/'composed-preview-2000.png')) +prior=np.asarray(Image.open(v1/'composed-preview-2000.png')) +assert np.array_equal(current[:2300],prior[:2300]) +hem_frame=(frame_alpha[2460:2630,:65]>0) +hem_foreground=fa[2460:2630,:65] +assert np.all(hem_foreground[hem_frame]==255) +report['structuralChecks']['textAndBackingPixelsIdenticalToV1']=True +report['structuralChecks']['upper2300RowsIdenticalToV1']=True +report['structuralChecks']['framePixelsExposedInsideCurtainHem']=int(np.count_nonzero(hem_frame & (hem_foreground<255))) +report['template']['v2Refinement']='Registered curtain hem occludes the frame through its actual lower edge; broad rounded cloud roll replaces the pointed tail, with continuous cubic tangents.' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +for name,box,size,canvas in [ + ('v1-v2-full-comparison',(0,0,2000,2800),(700,980),(1460,1060)), + ('v1-v2-junction-comparison',(0,2310,900,2780),(700,600),(1460,450)), +]: + board=Image.new('RGB',canvas,'#11161c');d=ImageDraw.Draw(board) + for col,(label,folder) in enumerate([('V1 — prior junction',v1),('V2 — hem and cloud roll',P)]): + d.text((col*730+20,18),label,font=label_font,fill='#f4ead4') + im=Image.open(folder/'composed-preview-2000.png').convert('RGB').crop(box);im.thumbnail(size);board.paste(im,(col*730+15,65)) + board.save(P/(name+'.png')) diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/cloud-textile-junction-details.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/cloud-textile-junction-details.png new file mode 100644 index 0000000..34593b7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/cloud-textile-junction-details.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-1000.png new file mode 100644 index 0000000..a01ab43 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-2000.png new file mode 100644 index 0000000..39bbab7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-500.png new file mode 100644 index 0000000..76bc7a1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview.svg new file mode 100644 index 0000000..18d756d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-comparison.png new file mode 100644 index 0000000..9e11996 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-junction-detail.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-junction-detail.png new file mode 100644 index 0000000..25f3f25 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/curtain-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/fonts.conf b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/fonts.conf new file mode 100644 index 0000000..1bd1f19 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/normal-prototype-v2/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.png new file mode 100644 index 0000000..65c80c3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.svg new file mode 100644 index 0000000..f61271e --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/layout-lineage-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/layout-lineage-comparison.png new file mode 100644 index 0000000..dbe1005 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/layout-lineage-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/preview-validation.json b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/preview-validation.json new file mode 100644 index 0000000..2c63d28 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/preview-validation.json @@ -0,0 +1,197 @@ +{ + "status": "passed", + "scope": "v05 Normal-only prototype \u2014 pearl cloud and suspended tent textile", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "M0 400H370 V1780H765 C765 1920 738 2010 724 2080 C720 2160 692 2180 704 2230 C711 2265 694 2294 677 2322 C664 2345 672 2385 695 2410 C721 2440 722 2478 701 2501 C680 2524 647 2527 619 2509 C594 2492 566 2496 537 2485 C510 2474 499 2459 471 2455 C436 2450 413 2442 393 2423 C377 2408 370 2375 370 2340 L369 2650 C318 2651 271 2644 224 2641 C173 2638 122 2650 76 2646 C45 2640 24 2634 0 2640Z", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 2653 + ], + "curtainFrameOrBackingOverlapPixels": 217818, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5", + "postPolicy": "Use the continuous entrance post already baked into v05 artwork; no new post layer", + "leftTextileAnchor": [ + 340, + 2106 + ], + "v2Refinement": "Registered curtain hem occludes the frame through its actual lower edge; broad rounded cloud roll replaces the pointed tail, with continuous cubic tangents." + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a", + "baseline": 2360, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 890, + 2306, + 1749, + 2362 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "prophet since in Israel like Moses,", + "baseline": 2468, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 769, + 2414, + 1869, + 2490 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "whom the LORD knew face to face", + "baseline": 2576, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 752, + 2521, + 1886, + 2578 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2690, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2649, + 1649, + 2707 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 752, + 2306, + 1886, + 2578 + ], + "upperFlourishToVerseInk": 66, + "verseInkToReferenceLineBox": 71, + "centeringDifference": 5, + "lineBaselineGap": 108 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "textPixelsIdenticalToCarriedForwardLayout": true, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "textAndBackingPixelsIdenticalToV1": true, + "upper2300RowsIdenticalToV1": true, + "framePixelsExposedInsideCurtainHem": 0 + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false, + "alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo" + }, + "foregroundAdaptation": "Liked nameplate geometry and suspended linen layout retained. New v05 pearl/cool cloud uses authored wave-like cubic contours; lower closure stays above source ground occlusion. The continuous entrance post is reused from the baked source and never redrawn." +} diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.png new file mode 100644 index 0000000..6d159c1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.svg new file mode 100644 index 0000000..02643e7 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/source-composed-comparison.png new file mode 100644 index 0000000..cabd0ef Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.png new file mode 100644 index 0000000..11508e0 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.svg new file mode 100644 index 0000000..789c094 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-full-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-full-comparison.png new file mode 100644 index 0000000..137eded Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-full-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-junction-comparison.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-junction-comparison.png new file mode 100644 index 0000000..2306e6c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/v1-v2-junction-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.png new file mode 100644 index 0000000..ea7151c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.svg new file mode 100644 index 0000000..f7f4d81 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not arisen a \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.png new file mode 100644 index 0000000..ac2914f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.svg new file mode 100644 index 0000000..cd91462 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-1-ink.svg @@ -0,0 +1 @@ +prophet since in Israel like Moses, \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.png new file mode 100644 index 0000000..c84272f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.svg new file mode 100644 index 0000000..bad6927 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/verse-2-ink.svg @@ -0,0 +1 @@ +whom the LORD knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-1000.png new file mode 100644 index 0000000..14607f7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-2000.png new file mode 100644 index 0000000..d97b5b1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-500.png new file mode 100644 index 0000000..7524bd3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain.svg b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain.svg new file mode 100644 index 0000000..96d6f3b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/normal-prototype-v2/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisen aprophet since in Israel like Moses,whom the LORD knew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/normal-reconstructed.png b/artifacts/cards/BP-001-moses/review/normal-reconstructed.png new file mode 100644 index 0000000..5b0d2e7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/normal-reconstructed.png differ diff --git a/artifacts/cards/BP-001-moses/review/post-before-after.png b/artifacts/cards/BP-001-moses/review/post-before-after.png new file mode 100644 index 0000000..683a34d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/post-before-after.png differ diff --git a/artifacts/cards/BP-001-moses/review/post-join-detail.png b/artifacts/cards/BP-001-moses/review/post-join-detail.png new file mode 100644 index 0000000..78a0ce8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/post-join-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/printings-comparison.png b/artifacts/cards/BP-001-moses/review/printings-comparison.png new file mode 100644 index 0000000..3e09a18 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/printings-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/source-validation.json b/artifacts/cards/BP-001-moses/review/source-validation.json new file mode 100644 index 0000000..191f200 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/source-validation.json @@ -0,0 +1,29 @@ +{ + "status": "pass", + "baseSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e", + "artSHA256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "dimensions": [ + 2000, + 2800 + ], + "changedPixels": 72357, + "changedFraction": 0.012920892857142856, + "differenceBoundsExclusive": [ + 318, + 994, + 377, + 2647 + ], + "authorizedRegion": [ + 310, + 994, + 379, + 2652 + ], + "allPixelsOutsidePostRegionIdentical": true, + "method": "Deterministic authored overlay, antialiased at 4x; RGB compositing only inside overlay alpha", + "scope": "Art-only, no card assembly", + "repeatableOutputs": true, + "repeatableFiles": 7, + "v04FilesUnchanged": 21 +} diff --git a/artifacts/cards/BP-001-moses/review/static-review.json b/artifacts/cards/BP-001-moses/review/static-review.json new file mode 100644 index 0000000..71b47b6 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/static-review.json @@ -0,0 +1,26 @@ +{ + "status": "passed-structural-user-visual-review-pending", + "cardId": "BP-001", + "revision": "v05", + "checks": { + "selectedNormalPreservedByteForByte": true, + "fourPrintingComposition": "passed", + "layerRegistration": "passed", + "opaqueFaces": "passed", + "textOnOpaqueBacking": "passed", + "foregroundTextIntersectionPixels": 0, + "depthResolvedFinishCoverage": "passed", + "textlessTentCloudOccludesTopLeftRail": "passed", + "textlessTopRailResumesOverOpenSky": "passed", + "lowMedHighExports": "passed" + }, + "reviewAssets": [ + "printings-comparison.png", + "layers-comparison.png", + "finish-masks-comparison.png", + "textile-finish-detail.png" + ], + "staticVisualAssessment": "Pending user review of the four production printings and masks.", + "movingLightReview": "Not performed.", + "finalCardApproval": null +} diff --git a/artifacts/cards/BP-001-moses/review/textile-finish-detail.png b/artifacts/cards/BP-001-moses/review/textile-finish-detail.png new file mode 100644 index 0000000..daacd8e Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/textile-finish-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/README.md new file mode 100644 index 0000000..904c242 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/README.md @@ -0,0 +1,24 @@ +# Moses v05 — vertical text textile prototype + +A separate Normal-only interpretation of the clarified lower-left layout. The horizontal footer is removed. A tall cream textile attaches to the outer left frame from mid-card to near the bottom, with a broad curved inner edge beside Moses and burgundy, lapis and gold woven bands. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [Horizontal / vertical comparison](horizontal-vertical-comparison.png) +- [Panel / robe junction details](panel-robe-junction-detail.png) +- [Source / composed comparison](source-composed-comparison.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The tent, cloud and curtain stay behind the lower text backing. No lower illustrated foreground is drawn, and no cloud crosses the lower panel or Moses. The upper nameplate/cloud-crown direction is retained. The curtain still occludes the outer rail, but that occlusion is composited beneath the lower panel. The continuous post remains part of the unmodified v05 source; no duplicate post is authored. + +The verse uses six lines at 68 master pixels, with a single-line reference at 42 pixels. Both use shared Sanctification P052 Medium; MOSES remains shared P052 Bold. Verse placement uses actual glyph bounds, with equal 154-master-pixel gaps from its upper ornament and reference ink. Exact narrator content remains unquoted and unchanged. + +Validation checks exact text, font manifest hashes, glyph coverage, safe bounds, opaque backing under every glyph, zero foreground/text intersection, zero lower foreground pixels, the unchanged source hash, and pixel preservation of the upper composition. The source remains registered at 2000 × 2800. Geometry-heavy outputs render at 2× and downsample in premultiplied alpha. + +Source SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v1/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Previous horizontal prototypes are preserved. No other printing types, finish masks, production layers, harness installation, acceptance or promotion are included. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.png new file mode 100644 index 0000000..b1775cc Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.svg new file mode 100644 index 0000000..85f10fc --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/build-preview.py new file mode 100644 index 0000000..f17ad2b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/build-preview.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M23 1400 H510 C592 1400 639 1610 653 1880 C668 2180 627 2490 585 2745 H23Z' +title_inner='' +verse_inner='''''' +for x in [125,215,305,395,485]: + verse_inner+=f'' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1444,2695])+'' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not',350,1764,68,'Sanctification P052',500), + ('verse-1','arisen a prophet',350,1884,68,'Sanctification P052',500), + ('verse-2','since in Israel',350,2004,68,'Sanctification P052',500), + ('verse-3','like Moses, whom',350,2124,68,'Sanctification P052',500), + ('verse-4','the LORD knew',350,2244,68,'Sanctification P052',500), + ('verse-5','face to face',350,2364,68,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',350,2550,42,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [80,2460,610,2600] if ident=='reference' else [65,1630,635,2410] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=1560;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only vertical lower-left textile prototype', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Tall lower-left textile attached to the outer frame; tent and cloud stay behind it; broad inner curve leaves Moses readable', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-only','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':120, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Upper nameplate/cloud crown retained. No lower cloud, tent, curtain or Moses foreground overlay; lower depth follows source art behind the opaque vertical textile.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'normal-prototype-v2' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('horizontal-vertical-comparison',[('Prior horizontal backing',previous/'composed-preview-1000.png'),('Tall lower-left backing',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,1450),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Textile, lettering and inner curve',(0,1340,820,2800),(20,65),(730,1300)), + ('Panel / robe separation',(490,1380,890,2500),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im.thumbnail(size);detail.paste(im,origin) +detail.save(P/'panel-robe-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert not np.any(fa[600:]) +prior=np.asarray(Image.open(previous/'composed-preview-2000.png')) +current=np.asarray(Image.open(P/'composed-preview-2000.png')) +assert np.array_equal(current[:1300],prior[:1300]) +report['structuralChecks']['upper1300RowsIdenticalToPrior']=True +report['structuralChecks']['lowerForegroundPixels']=int(np.count_nonzero(fa[600:])) +report['structuralChecks']['sourceMatchesV05CardSHA256']=True +report['structuralChecks']['secondPostDrawn']=False +report['template']['layoutReference']=str(previous.relative_to(REPO)) +report['template']['lowerDepth']='Tent, cloud and curtain remain source background behind the opaque vertical textile; no lower illustrated overflow' +report['template']['lowerTextLayout']='Six verse lines plus one reference line, centered by actual glyph bounds and measured vertical gaps' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-1000.png new file mode 100644 index 0000000..b2b6d65 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-2000.png new file mode 100644 index 0000000..66e5f91 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-500.png new file mode 100644 index 0000000..26a1d48 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview.svg new file mode 100644 index 0000000..2c3eaf9 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has notarisen a prophetsince in Israellike Moses, whomthe LORD knewface to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/fonts.conf new file mode 100644 index 0000000..fed6e62 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v1/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.png new file mode 100644 index 0000000..7e8ae24 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.svg new file mode 100644 index 0000000..b1e6d52 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/horizontal-vertical-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/horizontal-vertical-comparison.png new file mode 100644 index 0000000..86a5856 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/horizontal-vertical-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/panel-robe-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/panel-robe-junction-detail.png new file mode 100644 index 0000000..7c5f35d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/panel-robe-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/preview-validation.json new file mode 100644 index 0000000..de3a096 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/preview-validation.json @@ -0,0 +1,258 @@ +{ + "status": "passed", + "scope": "v05 Normal-only vertical lower-left textile prototype", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Tall lower-left textile attached to the outer frame; tent and cloud stay behind it; broad inner curve leaves Moses readable", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1400 H510 C592 1400 639 1610 653 1880 C668 2180 627 2490 585 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-only", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 493 + ], + "curtainFrameOrBackingOverlapPixels": 119446, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/normal-prototype-v2", + "lowerDepth": "Tent, cloud and curtain remain source background behind the opaque vertical textile; no lower illustrated overflow", + "lowerTextLayout": "Six verse lines plus one reference line, centered by actual glyph bounds and measured vertical gaps" + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not", + "baseline": 1764, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 80, + 1714, + 620, + 1766 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "arisen a prophet", + "baseline": 1884, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 107, + 1834, + 595, + 1904 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "since in Israel", + "baseline": 2004, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 148, + 1954, + 553, + 2006 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "like Moses, whom", + "baseline": 2124, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 79, + 2074, + 621, + 2135 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-4", + "text": "the LORD knew", + "baseline": 2244, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 107, + 2194, + 593, + 2246 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-5", + "text": "face to face", + "baseline": 2364, + "font": "Sanctification P052", + "weight": 500, + "size": 68, + "inkBounds": [ + 187, + 2314, + 513, + 2366 + ], + "safeBounds": [ + 65, + 1630, + 635, + 2410 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2550, + "font": "Sanctification P052", + "weight": 500, + "size": 42, + "inkBounds": [ + 108, + 2520, + 593, + 2563 + ], + "safeBounds": [ + 80, + 2460, + 610, + 2600 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 79, + 1714, + 621, + 2366 + ], + "upperFlourishToVerseInk": 154, + "verseInkToReferenceLineBox": 154, + "centeringDifference": 0, + "lineBaselineGap": 120 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1300RowsIdenticalToPrior": true, + "lowerForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Upper nameplate/cloud crown retained. No lower cloud, tent, curtain or Moses foreground overlay; lower depth follows source art behind the opaque vertical textile." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.png new file mode 100644 index 0000000..11b425b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.svg new file mode 100644 index 0000000..a7ee0ee --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/source-composed-comparison.png new file mode 100644 index 0000000..6b4f1e2 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.png new file mode 100644 index 0000000..70487e3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.svg new file mode 100644 index 0000000..510075e --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has notarisen a prophetsince in Israellike Moses, whomthe LORD knewface to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.png new file mode 100644 index 0000000..3521d17 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.svg new file mode 100644 index 0000000..f65e685 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.png new file mode 100644 index 0000000..fd970f3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.svg new file mode 100644 index 0000000..ad18122 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-1-ink.svg @@ -0,0 +1 @@ +arisen a prophet \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.png new file mode 100644 index 0000000..db3d5f3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.svg new file mode 100644 index 0000000..9f92b18 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-2-ink.svg @@ -0,0 +1 @@ +since in Israel \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.png new file mode 100644 index 0000000..33109af Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.svg new file mode 100644 index 0000000..73aa338 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-3-ink.svg @@ -0,0 +1 @@ +like Moses, whom \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.png new file mode 100644 index 0000000..178f2a5 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.svg new file mode 100644 index 0000000..67aee71 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-4-ink.svg @@ -0,0 +1 @@ +the LORD knew \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.png new file mode 100644 index 0000000..b8ce822 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.svg new file mode 100644 index 0000000..04e7329 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/verse-5-ink.svg @@ -0,0 +1 @@ +face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-1000.png new file mode 100644 index 0000000..e2fb0b5 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-2000.png new file mode 100644 index 0000000..d9cfc85 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-500.png new file mode 100644 index 0000000..8936747 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain.svg new file mode 100644 index 0000000..e8e991a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v1/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has notarisen a prophetsince in Israellike Moses, whomthe LORD knewface to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/README.md new file mode 100644 index 0000000..6923e56 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/README.md @@ -0,0 +1,27 @@ +# Moses v05 — pitched textile with robe foreground + +A separate Normal-only revision of the vertical layout; v1 remains intact. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V1 / V2 comparison](v1-v2-comparison.png) +- [Panel / robe junction detail](panel-robe-junction-detail.png) +- [Linen texture detail](linen-texture-detail.png) +- [Source / composed comparison](source-composed-comparison.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The cream backing is now a broad pitched textile extending rightward to x1120 beneath Moses. Its outline is a simple tensioned polygon, not a contour cut around his body. A separate, source-registered robe foreground restores Moses above the backing. The background cloud and tent stay behind it. + +A reinforced sloping seam, stitching and small tie points replace the generic top rule/flourish. Fine low-opacity linen fibers add quiet material texture. Burgundy, lapis and gold remain restrained woven accents along the seams and hem; there is no repeating diamond field pattern. + +The usable text area is wider. Exact narrator text is set in five lines at 72 master pixels; the reference remains on one line at 48 pixels. All lettering uses shared P052 roles, with measured 165px spacing above and below the verse ink. The liked upper nameplate/cloud direction is preserved. + +Validation covers exact text and reference, font hashes/glyph coverage, measured safe bounds, opaque backing beneath all glyphs, zero foreground/text intersection, unchanged v05 source hash, and pixel preservation of the upper composition. The robe clip includes the textile stroke and shadow as well as its fill, preventing a seam fringe from crossing the figure. Its contour excludes the background entrance post. Static review includes the full card, panel/robe edge and enlarged texture. + +Source: ../../source/art-master.png. Unchanged SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. No duplicate entrance post is drawn. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v2/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. Geometry-heavy outputs render at 2× and downsample in premultiplied alpha. + +Normal prototype only. No other printings, finish masks, production layers, harness installation, acceptance or promotion. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.png new file mode 100644 index 0000000..ef6dcb0 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.svg new file mode 100644 index 0000000..3ecc1d4 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/build-preview.py new file mode 100644 index 0000000..b2bb294 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/build-preview.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M23 1500 L380 1320 L1120 1640 L1070 2745 H23Z' +title_inner='' +# Reinforced pitched seam, construction stitches and a restrained woven hem. +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +defs=defs.replace('','''''') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1500,2695])+'' +ties+='' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# Moses is in front of the new textile. The contour follows his robe's +# broad outer leading, while the textile itself is a pitched polygon. +robe_edge='M670 1280 C650 1390 671 1540 725 1650 C756 1730 797 1850 783 1950 C776 2050 731 2170 716 2290 C708 2400 717 2510 710 2610 C701 2710 680 2760 650 2800' +robe_path=robe_edge+' H2000V1280Z' +robe_footprint='M17 1497 L380 1308 L1132 1634 L1080 2753 H17Z' +defs=defs.replace('',f'') +robe=f'' +curtain+=robe +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not',410,1818,72,'Sanctification P052',500), + ('verse-1','arisen a prophet since',410,1938,72,'Sanctification P052',500), + ('verse-2','in Israel like Moses,',410,2058,72,'Sanctification P052',500), + ('verse-3','whom the LORD',410,2178,72,'Sanctification P052',500), + ('verse-4','knew face to face',410,2298,72,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',410,2500,48,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [100,2410,705,2600] if ident=='reference' else [60,1660,775,2380] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=1600;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only flared tent textile with Moses foreground', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Pitched linen textile extends beneath Moses; his restored robe establishes foreground depth; background cloud stays behind the field', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-and-Moses-robe','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':120, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Upper nameplate/cloud retained. Moses robe restored above the pitched textile using a smooth source-registered contour clipped to the textile footprint. No lower cloud or background tent foreground.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'vertical-text-prototype-v1' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('v1-v2-comparison',[('V1 — narrow vertical backing',previous/'composed-preview-1000.png'),('V2 — pitched textile / robe',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,1450),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Pitched textile and lettering',(0,1260,1140,2800),(20,65),(730,1300)), + ('Robe over the textile',(570,1380,1050,2550),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im.thumbnail(size);detail.paste(im,origin) +detail.save(P/'panel-robe-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert np.count_nonzero(fa[1400:])>100000 +prior=np.asarray(Image.open(previous/'composed-preview-2000.png')) +current=np.asarray(Image.open(P/'composed-preview-2000.png')) +assert np.array_equal(current[:1200],prior[:1200]) +report['structuralChecks']['upper1200RowsIdenticalToPrior']=True +report['structuralChecks']['robeForegroundPixels']=int(np.count_nonzero(fa[1400:])) +report['structuralChecks']['lowerCloudForegroundPixels']=0 +report['structuralChecks']['sourceMatchesV05CardSHA256']=True +report['structuralChecks']['secondPostDrawn']=False +report['template']['layoutReference']=str(previous.relative_to(REPO)) +report['template']['lowerDepth']='Background tent, curtain and cloud behind textile; Moses robe above textile' +report['template']['lowerTextLayout']='Five 72px verse lines plus one 48px reference line; centered by actual glyph bounds' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') + +texture=master.crop((80,1550,600,1800));texture=texture.resize((1040,500),Image.Resampling.NEAREST);texture.save(P/'linen-texture-detail.png') +report['template']['robeContour']=robe_edge +report['template']['robeOcclusionFootprint']=robe_footprint +report['template']['robeEdgePolicy']='Contour stays inside Moses blue/ivory robe leading; background post excluded; footprint covers textile stroke and shadow as well as fill' +report['template']['textileGeometry']='Broad pitched polygon extending to x1120 beneath Moses; no silhouette-shaped backing cutout' +report['template']['texture']='Low-opacity horizontal/vertical linen fibers and sparse light slubs; no diamond field pattern' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') + +# The complete pitched edge (including stroke/shadow fringe) stays behind Moses. +seam_samples=[] +for x in range(740,1061,8): + y=round(1320+(x-380)*320/740) + for dy in [-4,0,4]: + assert fa[y+dy,x]==255,(x,y+dy,'textile seam not fully occluded') + seam_samples.append([x,y+dy]) +report['structuralChecks']['pitchedSeamFullyBehindRobe']=True +report['structuralChecks']['opaqueSeamSamples']=len(seam_samples) +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-1000.png new file mode 100644 index 0000000..7f34441 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-2000.png new file mode 100644 index 0000000..971b6b0 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-500.png new file mode 100644 index 0000000..fdec4a2 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview.svg new file mode 100644 index 0000000..733c6e9 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/fonts.conf new file mode 100644 index 0000000..1a67183 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v2/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.png new file mode 100644 index 0000000..57fd138 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.svg new file mode 100644 index 0000000..6268332 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/linen-texture-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/linen-texture-detail.png new file mode 100644 index 0000000..a4a5b72 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/linen-texture-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/panel-robe-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/panel-robe-junction-detail.png new file mode 100644 index 0000000..f821911 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/panel-robe-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/preview-validation.json new file mode 100644 index 0000000..dd1ed39 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/preview-validation.json @@ -0,0 +1,244 @@ +{ + "status": "passed", + "scope": "v05 Normal-only flared tent textile with Moses foreground", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Pitched linen textile extends beneath Moses; his restored robe establishes foreground depth; background cloud stays behind the field", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1500 L380 1320 L1120 1640 L1070 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-and-Moses-robe", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1135, + 2756 + ], + "curtainFrameOrBackingOverlapPixels": 570043, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v1", + "lowerDepth": "Background tent, curtain and cloud behind textile; Moses robe above textile", + "lowerTextLayout": "Five 72px verse lines plus one 48px reference line; centered by actual glyph bounds", + "robeContour": "M670 1280 C650 1390 671 1540 725 1650 C756 1730 797 1850 783 1950 C776 2050 731 2170 716 2290 C708 2400 717 2510 710 2610 C701 2710 680 2760 650 2800", + "robeOcclusionFootprint": "M17 1497 L380 1308 L1132 1634 L1080 2753 H17Z", + "robeEdgePolicy": "Contour stays inside Moses blue/ivory robe leading; background post excluded; footprint covers textile stroke and shadow as well as fill", + "textileGeometry": "Broad pitched polygon extending to x1120 beneath Moses; no silhouette-shaped backing cutout", + "texture": "Low-opacity horizontal/vertical linen fibers and sparse light slubs; no diamond field pattern" + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not", + "baseline": 1818, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 124, + 1765, + 696, + 1820 + ], + "safeBounds": [ + 60, + 1660, + 775, + 2380 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "arisen a prophet since", + "baseline": 1938, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 64, + 1885, + 756, + 1959 + ], + "safeBounds": [ + 60, + 1660, + 775, + 2380 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "in Israel like Moses,", + "baseline": 2058, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 99, + 2005, + 720, + 2070 + ], + "safeBounds": [ + 60, + 1660, + 775, + 2380 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "whom the LORD", + "baseline": 2178, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 138, + 2125, + 681, + 2180 + ], + "safeBounds": [ + 60, + 1660, + 775, + 2380 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-4", + "text": "knew face to face", + "baseline": 2298, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 139, + 2245, + 680, + 2300 + ], + "safeBounds": [ + 60, + 1660, + 775, + 2380 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2500, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 133, + 2465, + 687, + 2514 + ], + "safeBounds": [ + 100, + 2410, + 705, + 2600 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 64, + 1765, + 756, + 2300 + ], + "upperFlourishToVerseInk": 165, + "verseInkToReferenceLineBox": 165, + "centeringDifference": 0, + "lineBaselineGap": 120 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1200RowsIdenticalToPrior": true, + "robeForegroundPixels": 466562, + "lowerCloudForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "pitchedSeamFullyBehindRobe": true, + "opaqueSeamSamples": 123 + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Upper nameplate/cloud retained. Moses robe restored above the pitched textile using a smooth source-registered contour clipped to the textile footprint. No lower cloud or background tent foreground." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.png new file mode 100644 index 0000000..c192915 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.svg new file mode 100644 index 0000000..8faa391 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/source-composed-comparison.png new file mode 100644 index 0000000..def78e1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.png new file mode 100644 index 0000000..eefee4f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.svg new file mode 100644 index 0000000..4a8fc5b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/v1-v2-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/v1-v2-comparison.png new file mode 100644 index 0000000..7a21090 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/v1-v2-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.png new file mode 100644 index 0000000..3c3fa30 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.svg new file mode 100644 index 0000000..238e99c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.png new file mode 100644 index 0000000..2a71f5a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.svg new file mode 100644 index 0000000..1cfbe9b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-1-ink.svg @@ -0,0 +1 @@ +arisen a prophet since \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.png new file mode 100644 index 0000000..94ee0e7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.svg new file mode 100644 index 0000000..9430d81 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-2-ink.svg @@ -0,0 +1 @@ +in Israel like Moses, \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.png new file mode 100644 index 0000000..d6aa3fd Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.svg new file mode 100644 index 0000000..14c4af8 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-3-ink.svg @@ -0,0 +1 @@ +whom the LORD \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.png new file mode 100644 index 0000000..b0d1ed8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.svg new file mode 100644 index 0000000..3772078 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/verse-4-ink.svg @@ -0,0 +1 @@ +knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-1000.png new file mode 100644 index 0000000..bc86f16 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-2000.png new file mode 100644 index 0000000..adcd701 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-500.png new file mode 100644 index 0000000..13d823c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain.svg new file mode 100644 index 0000000..2fc4116 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v2/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/README.md new file mode 100644 index 0000000..c263e59 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/README.md @@ -0,0 +1,27 @@ +# Moses v05 — foreground backing, selected sleeve only + +A separate Normal-only revision; v2 remains intact. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V2 / V3 comparison](v2-v3-comparison.png) +- [Panel silhouette / sleeve detail](panel-sleeve-junction-detail.png) +- [Linen texture detail](linen-texture-detail.png) +- [Source / composed comparison](source-composed-comparison.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The backing is now the foreground structure where it overlaps Moses. The previous full-robe extraction is removed. Only a small blue sleeve tip crosses the upper-right textile seam; no lower robe, ivory tunic, post or background is restored over the backing. + +The textile starts lower, with its peak lowered from y1320 to y1630 and its left start from y1500 to y1740. Its tent pitch is shallower. Reinforced seams, small ties, quiet linen fibers and restrained burgundy/lapis/gold hem trim remain. The upper nameplate/cloud crown and source artwork are preserved. + +The exact excerpt remains five 72px P052 Medium lines, now centered in the wider visible field. The reference stays on one 48px line. Actual verse ink has balanced 140px gaps above and below. Title remains P052 Bold. + +Validation checks exact content, font hashes/glyph coverage, safe glyph bounds, opaque backing, zero sleeve/text intersection, unchanged source hash, preserved upper composition, restricted sleeve coverage and zero illustrated foreground below y1810. That final check prevents the full robe or tunic from reappearing above the lower panel. + +Source SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. The baked entrance post is not drawn again. Geometry-heavy outputs render at 2× and downsample with premultiplied alpha. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v3/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Normal review prototype only. No other printings, masks, production layers, harness installation, acceptance or promotion. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.png new file mode 100644 index 0000000..dc09d7b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.svg new file mode 100644 index 0000000..98e6135 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/build-preview.py new file mode 100644 index 0000000..c10cf3a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/build-preview.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z' +title_inner='' +# Reinforced pitched seam, construction stitches and a restrained woven hem. +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +defs=defs.replace('','''''') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1740,2695])+'' +ties+='' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# The card/backing stays in front of Moses. Only this small blue sleeve tip +# crosses its upper-right seam; no lower robe, tunic or background is restored. +sleeve_edge='M720 1640 C734 1692 754 1742 779 1786 C783 1794 790 1791 794 1780 C807 1747 820 1705 841 1650' +sleeve_path=sleeve_edge+' L860 1580H710Z' +sleeve_footprint='M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z' +defs=defs.replace('',f'') +sleeve=f'' +curtain+=sleeve +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not',540,1973,72,'Sanctification P052',500), + ('verse-1','arisen a prophet since',540,2093,72,'Sanctification P052',500), + ('verse-2','in Israel like Moses,',540,2213,72,'Sanctification P052',500), + ('verse-3','whom the LORD',540,2333,72,'Sanctification P052',500), + ('verse-4','knew face to face',540,2453,72,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',540,2630,48,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [200,2570,870,2690] if ident=='reference' else [120,1840,940,2510] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=1780;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only foreground textile with limited blue sleeve overlap', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Lower, shallower tent textile overlays Moses; only a selected blue sleeve tip hangs over its seam', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-and-selected-blue-sleeve-only','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':120, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Upper nameplate/cloud retained. Backing is in front of Moses, with only a small blue sleeve tip above its upper seam. Lower robe, ivory tunic, post and background are never restored over the panel.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'vertical-text-prototype-v2' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('v2-v3-comparison',[('V2 — full robe foreground',previous/'composed-preview-1000.png'),('V3 — backing / sleeve tip',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,950),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Lower, shallow tent textile',(0,1540,1160,2800),(20,65),(730,1300)), + ('Selected blue sleeve only',(630,1560,990,1900),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((im.width*2,im.height*2),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +d.text((845,615),'Subtle linen fibers',font=label_font,fill='#f4ead4') +texture_strip=master.crop((80,1820,620,1910));texture_strip.thumbnail((520,180));detail.paste(texture_strip,(845,660)) +detail.save(P/'panel-sleeve-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert 1000 + + + + + +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/fonts.conf new file mode 100644 index 0000000..e128201 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v3/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.png new file mode 100644 index 0000000..7aed23f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.svg new file mode 100644 index 0000000..3b28ead --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/linen-texture-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/linen-texture-detail.png new file mode 100644 index 0000000..dd7eb6c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/linen-texture-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/panel-sleeve-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/panel-sleeve-junction-detail.png new file mode 100644 index 0000000..eeb803e Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/panel-sleeve-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/preview-validation.json new file mode 100644 index 0000000..61caa81 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/preview-validation.json @@ -0,0 +1,243 @@ +{ + "status": "passed", + "scope": "v05 Normal-only foreground textile with limited blue sleeve overlap", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Lower, shallower tent textile overlays Moses; only a selected blue sleeve tip hangs over its seam", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-and-selected-blue-sleeve-only", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 1796 + ], + "curtainFrameOrBackingOverlapPixels": 123961, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v2", + "lowerDepth": "Backing overlays Moses; only selected blue sleeve tip above the upper seam. Entire lower robe/tunic remains behind the backing.", + "lowerTextLayout": "Five 72px verse lines plus one 48px reference line; centered by actual glyph bounds", + "sleeveContour": "M720 1640 C734 1692 754 1742 779 1786 C783 1794 790 1791 794 1780 C807 1747 820 1705 841 1650", + "sleeveOcclusionFootprint": "M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z", + "textileGeometry": "Shallow pitched polygon; top peak lowered from y1320 to y1630, left start from y1500 to y1740", + "texture": "Low-opacity woven linen fibers; reinforced pitched seam and ties retained" + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not", + "baseline": 1973, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 254, + 1920, + 826, + 1975 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "arisen a prophet since", + "baseline": 2093, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 194, + 2040, + 886, + 2114 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "in Israel like Moses,", + "baseline": 2213, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 229, + 2160, + 850, + 2225 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "whom the LORD", + "baseline": 2333, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 268, + 2280, + 811, + 2335 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-4", + "text": "knew face to face", + "baseline": 2453, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 269, + 2400, + 810, + 2455 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2630, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 263, + 2595, + 817, + 2644 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 194, + 1920, + 886, + 2455 + ], + "upperFlourishToVerseInk": 140, + "verseInkToReferenceLineBox": 140, + "centeringDifference": 0, + "lineBaselineGap": 120 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1200RowsIdenticalToPrior": true, + "selectedSleeveForegroundPixels": 5353, + "foregroundPixelsBelow1810": 0, + "lowerCloudForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "noFullRobeOrTunicForeground": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Upper nameplate/cloud retained. Backing is in front of Moses, with only a small blue sleeve tip above its upper seam. Lower robe, ivory tunic, post and background are never restored over the panel." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.png new file mode 100644 index 0000000..ad24432 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.svg new file mode 100644 index 0000000..48b34c7 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/source-composed-comparison.png new file mode 100644 index 0000000..c023447 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.png new file mode 100644 index 0000000..5414f2c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.svg new file mode 100644 index 0000000..0856eca --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/v2-v3-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/v2-v3-comparison.png new file mode 100644 index 0000000..a5ebe6c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/v2-v3-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.png new file mode 100644 index 0000000..71441fe Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.svg new file mode 100644 index 0000000..f908f09 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.png new file mode 100644 index 0000000..49ea5fd Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.svg new file mode 100644 index 0000000..ad7694f --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-1-ink.svg @@ -0,0 +1 @@ +arisen a prophet since \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.png new file mode 100644 index 0000000..8e28a59 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.svg new file mode 100644 index 0000000..ba82a32 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-2-ink.svg @@ -0,0 +1 @@ +in Israel like Moses, \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.png new file mode 100644 index 0000000..ddbcd2d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.svg new file mode 100644 index 0000000..d811ec5 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-3-ink.svg @@ -0,0 +1 @@ +whom the LORD \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.png new file mode 100644 index 0000000..4408079 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.svg new file mode 100644 index 0000000..b3e9b06 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/verse-4-ink.svg @@ -0,0 +1 @@ +knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-1000.png new file mode 100644 index 0000000..c0d964f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-2000.png new file mode 100644 index 0000000..744ebd3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-500.png new file mode 100644 index 0000000..3677e5d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain.svg new file mode 100644 index 0000000..9b81b46 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v3/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has notarisen a prophet sincein Israel like Moses,whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/README.md new file mode 100644 index 0000000..6969ced --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/README.md @@ -0,0 +1,26 @@ +# Moses v05 — complete folded sleeve foreground + +A separate Normal-only revision; v3 remains intact. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V3 / V4 comparison](v3-v4-comparison.png) +- [Source / selected sleeve / composed junction](sleeve-selection-detail.png) +- [Panel and sleeve detail](panel-sleeve-junction-detail.png) +- [Linen texture detail](linen-texture-detail.png) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The foreground now includes the coherent folded arm garment: the blue outer sleeve, its integral cuff/edge glass, and the red inner lining returning diagonally from the low V toward the wrist. The extraction ends at that V near y2020. The separate descending red robe shard below it remains covered by the backing, as do the lower robe, ivory tunic, post and background. + +The same tent backing shape, material, seams and ties are retained pixel for pixel. The upper nameplate/cloud crown is preserved. The source artwork is unchanged and remains registered at 2000 × 2800. + +The exact narrator text is rewrapped into five 72px P052 Medium lines beginning “And there has” / “not arisen a prophet”. This shorter first line clears the full sleeve without pushing the paragraph far off-center. The one-line reference stays 48px. Measured verse spacing remains balanced at 140px above and below the actual ink. + +Validation checks exact content, font hashes/glyph coverage, measured safe bounds, opaque backing, zero sleeve/text intersection with a verified 20-master-pixel clearance, foreground component bounds, substantial blue and red coverage, zero foreground in the excluded lower-shard region, unchanged source hash, and preserved backing/upper composition. The three-way selection detail includes a dashed y2030 guide: all sleeve foreground ends above it, while the lower red shard is visible only in the source view. + +Source SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. No duplicate post is drawn. Geometry-heavy outputs render at 2× and downsample with premultiplied alpha. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v4/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Normal review prototype only. No other printings, finish masks, production layers, harness installation, acceptance or promotion. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.png new file mode 100644 index 0000000..dc09d7b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.svg new file mode 100644 index 0000000..b34e840 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/build-preview.py new file mode 100644 index 0000000..12b468b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/build-preview.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z' +title_inner='' +# Reinforced pitched seam, construction stitches and a restrained woven hem. +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +defs=defs.replace('','''''') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1740,2695])+'' +ties+='' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# The card/backing stays in front of Moses. Only this small blue sleeve tip +# crosses its upper-right seam; no lower robe, tunic or background is restored. +sleeve_edge='M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480' +sleeve_path=sleeve_edge+' L1120 1410 L1080 1330 L900 1190H650Z' +sleeve_footprint='M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z' +defs=defs.replace('',f'') +sleeve=f'' +curtain+=sleeve +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has',530,1983,72,'Sanctification P052',500), + ('verse-1','not arisen a prophet',530,2103,72,'Sanctification P052',500), + ('verse-2','since in Israel like',530,2223,72,'Sanctification P052',500), + ('verse-3','Moses, whom the LORD',530,2343,72,'Sanctification P052',500), + ('verse-4','knew face to face',530,2463,72,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',530,2640,48,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [200,2570,870,2690] if ident=='reference' else [120,1840,940,2510] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=1790;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only foreground textile with complete blue/red arm fold', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-and-complete-blue-red-arm-fold','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':120, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'vertical-text-prototype-v3' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('v3-v4-comparison',[('V3 — small blue tip',previous/'composed-preview-1000.png'),('V4 — complete blue/red fold',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,1300),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Lower, shallow tent textile',(0,1540,1160,2800),(20,65),(730,1300)), + ('Full arm fold; lower shard covered',(650,1510,1140,2240),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((im.width*2,im.height*2),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +d.text((845,1060),'Subtle linen fibers',font=label_font,fill='#f4ead4') +texture_strip=master.crop((80,1820,620,1910));texture_strip.thumbnail((520,180));detail.paste(texture_strip,(845,1100)) +detail.save(P/'panel-sleeve-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert 120000) +component_bounds=[int(xs.min()),int(ys.min()+600),int(xs.max()+1),int(ys.max()+601)] +fg_rgb=np.asarray(fg)[:,:,:3].astype(float) +component=(fa>0);component[:600]=False +red=component&(fg_rgb[:,:,0]>1.35*fg_rgb[:,:,2])&(fg_rgb[:,:,0]>1.25*fg_rgb[:,:,1])&(fg_rgb[:,:,0]>65) +blue=component&(fg_rgb[:,:,2]>1.4*fg_rgb[:,:,0])&(fg_rgb[:,:,2]>50) +assert np.count_nonzero(red)>1000 and np.count_nonzero(blue)>1000 +assert component_bounds[3]<=2030 +assert not np.any(fa[2040:2420,790:1040]) +assert np.array_equal(np.asarray(Image.open(P/'border-backing-preview.png')),np.asarray(Image.open(previous/'border-backing-preview.png'))) +report['structuralChecks']['sleeveComponentBounds']=component_bounds +report['structuralChecks']['redLiningPixels']=int(np.count_nonzero(red)) +report['structuralChecks']['blueSleevePixels']=int(np.count_nonzero(blue)) +report['structuralChecks']['excludedLowerRedShardForegroundPixels']=0 +report['structuralChecks']['backingPixelsIdenticalToV3']=True +report['template']['sleeveScope']='Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +# Three-way junction review makes both inclusion and exclusion explicit. +box=(650,1510,1150,2300) +board=Image.new('RGB',(1830,1120),'#11161c');d=ImageDraw.Draw(board) +source=Image.open(ART).convert('RGBA').crop(box) +selected=Image.alpha_composite(Image.new('RGBA',source.size,'#f4ead4'),fg.crop(box)) +composed=Image.open(P/'composed-preview-2000.png').convert('RGBA').crop(box) +for col,(label,im) in enumerate([('Source arm / descending shard',source),('Selected sleeve foreground',selected),('Backing covers lower shard',composed)]): + d.text((col*610+16,16),label,font=label_font,fill='#f4ead4') + im=im.resize((570,901),Image.Resampling.LANCZOS).convert('RGB');board.paste(im,(col*610+15,65)) + line_y=65+round((2030-box[1])*901/(box[3]-box[1])) + for x in range(col*610+15,col*610+585,18):d.line((x,line_y,x+9,line_y),fill='#dbac66',width=2) + d.text((col*610+16,995),'Dashed line: foreground ends above',font=label_font,fill='#d9c9a8') +board.save(P/'sleeve-selection-detail.png') + +# A 20px square clearance around the sleeve must contain no glyph ink. +local_text=(ta[1500:2100,650:1100]>0).astype(np.int32) +local_foreground=fa[1500:2100,650:1100]>0 +integral=np.pad(local_text,((21,20),(21,20))).cumsum(0).cumsum(1) +expanded=integral[41:,41:]-integral[:-41,41:]-integral[41:,:-41]+integral[:-41,:-41] +assert not np.any((expanded>0)&local_foreground) +report['structuralChecks']['sleeveGlyphClearanceAtLeastMasterPixels']=20 +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-1000.png new file mode 100644 index 0000000..1d8b483 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-2000.png new file mode 100644 index 0000000..e7a3a9a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-500.png new file mode 100644 index 0000000..77249ee Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview.svg new file mode 100644 index 0000000..e89b22d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there hasnot arisen a prophetsince in Israel likeMoses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/fonts.conf new file mode 100644 index 0000000..9ff9d4b --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v4/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.png new file mode 100644 index 0000000..f1dfc90 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.svg new file mode 100644 index 0000000..d878b21 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/linen-texture-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/linen-texture-detail.png new file mode 100644 index 0000000..dd7eb6c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/linen-texture-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/panel-sleeve-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/panel-sleeve-junction-detail.png new file mode 100644 index 0000000..965447a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/panel-sleeve-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/preview-validation.json new file mode 100644 index 0000000..30f3142 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/preview-validation.json @@ -0,0 +1,255 @@ +{ + "status": "passed", + "scope": "v05 Normal-only foreground textile with complete blue/red arm fold", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-and-complete-blue-red-arm-fold", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 2026 + ], + "curtainFrameOrBackingOverlapPixels": 158854, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v3", + "lowerDepth": "Backing overlays Moses except for the complete folded arm garment. Separate descending red shard and all lower robe/tunic remain behind.", + "lowerTextLayout": "Five 72px verse lines plus one 48px reference line; centered by actual glyph bounds", + "sleeveContour": "M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480", + "sleeveOcclusionFootprint": "M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z", + "textileGeometry": "Shallow pitched polygon; top peak lowered from y1320 to y1630, left start from y1500 to y1740", + "texture": "Low-opacity woven linen fibers; reinforced pitched seam and ties retained", + "sleeveScope": "Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it" + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has", + "baseline": 1983, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 305, + 1930, + 753, + 1985 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "not arisen a prophet", + "baseline": 2103, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 210, + 2050, + 850, + 2124 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "since in Israel like", + "baseline": 2223, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 249, + 2170, + 810, + 2225 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "Moses, whom the LORD", + "baseline": 2343, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 140, + 2290, + 920, + 2355 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-4", + "text": "knew face to face", + "baseline": 2463, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 259, + 2410, + 800, + 2465 + ], + "safeBounds": [ + 120, + 1840, + 940, + 2510 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2640, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 253, + 2605, + 807, + 2654 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 140, + 1930, + 920, + 2465 + ], + "upperFlourishToVerseInk": 140, + "verseInkToReferenceLineBox": 140, + "centeringDifference": 0, + "lineBaselineGap": 120 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1200RowsIdenticalToPrior": true, + "selectedSleeveForegroundPixels": 41519, + "foregroundPixelsBelow2030": 0, + "lowerCloudForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "noFullRobeOrTunicForeground": true, + "sleeveComponentBounds": [ + 735, + 1689, + 977, + 2026 + ], + "redLiningPixels": 17962, + "blueSleevePixels": 4266, + "excludedLowerRedShardForegroundPixels": 0, + "backingPixelsIdenticalToV3": true, + "sleeveGlyphClearanceAtLeastMasterPixels": 20 + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.png new file mode 100644 index 0000000..6966d29 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.svg new file mode 100644 index 0000000..021064c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/sleeve-selection-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/sleeve-selection-detail.png new file mode 100644 index 0000000..b5ab219 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/sleeve-selection-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/source-composed-comparison.png new file mode 100644 index 0000000..92324b8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.png new file mode 100644 index 0000000..84ea4b8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.svg new file mode 100644 index 0000000..0feff4f --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there hasnot arisen a prophetsince in Israel likeMoses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/v3-v4-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/v3-v4-comparison.png new file mode 100644 index 0000000..5d99054 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/v3-v4-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.png new file mode 100644 index 0000000..f55f792 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.svg new file mode 100644 index 0000000..5a50a5c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-0-ink.svg @@ -0,0 +1 @@ +And there has \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.png new file mode 100644 index 0000000..a48b56f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.svg new file mode 100644 index 0000000..d61989a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-1-ink.svg @@ -0,0 +1 @@ +not arisen a prophet \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.png new file mode 100644 index 0000000..6ba24b8 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.svg new file mode 100644 index 0000000..602f30c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-2-ink.svg @@ -0,0 +1 @@ +since in Israel like \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.png new file mode 100644 index 0000000..1e19cd7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.svg new file mode 100644 index 0000000..2fcacd6 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-3-ink.svg @@ -0,0 +1 @@ +Moses, whom the LORD \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.png new file mode 100644 index 0000000..68b0bbb Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.svg new file mode 100644 index 0000000..6dd95ff --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/verse-4-ink.svg @@ -0,0 +1 @@ +knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-1000.png new file mode 100644 index 0000000..8774249 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-2000.png new file mode 100644 index 0000000..f2f6ce3 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-500.png new file mode 100644 index 0000000..31cddc9 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain.svg new file mode 100644 index 0000000..f28ee5a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v4/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there hasnot arisen a prophetsince in Israel likeMoses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/README.md new file mode 100644 index 0000000..6047cdc --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/README.md @@ -0,0 +1,27 @@ +# Moses v05 — semantic four-line verse + +Typography-only revision of v4, preserved separately. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V4 / V5 text comparison](v4-v5-text-comparison.png) +- [V4 / V5 full comparison](v4-v5-comparison.png) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The verse now uses the requested semantic wrap: + +And there has not arisen
+a prophet since in Israel
+like Moses, whom the LORD
+knew face to face + +Shared Sanctification P052 Medium is set at 66 master pixels with natural font spacing. No tracking compression or horizontal scaling is used. The four-line actual ink block is centered in the usable band below the complete sleeve and above the unchanged single-line reference, with 84 master pixels above and below. + +Only the lower verse typography changes. Source art, backing, linen texture, frame, full blue/red sleeve, title and reference remain unchanged. Validation compares backing, foreground, title and reference pixels directly with v4, and verifies zero changed composed pixels outside the old/new text support. It also checks exact text, shared font hashes/glyph coverage, safe bounds, opaque backing, zero sleeve/text intersection, and the unchanged source hash. + +Source SHA256: 372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee. + +Rebuild: python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v5/build-preview.py. Requires Inkscape, Fontconfig, Pillow and NumPy. + +Normal prototype only. No other printings, finish masks, production layers, harness installation, acceptance or promotion. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.png new file mode 100644 index 0000000..dc09d7b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.svg new file mode 100644 index 0000000..b34e840 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/build-preview.py new file mode 100644 index 0000000..13eb2be --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/build-preview.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont,ImageFilter + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z' +title_inner='' +# Reinforced pitched seam, construction stitches and a restrained woven hem. +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +defs=defs.replace('','''''') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1740,2695])+'' +ties+='' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# The card/backing stays in front of Moses. Only this small blue sleeve tip +# crosses its upper-right seam; no lower robe, tunic or background is restored. +sleeve_edge='M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480' +sleeve_path=sleeve_edge+' L1120 1410 L1080 1330 L900 1190H650Z' +sleeve_footprint='M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z' +defs=defs.replace('',f'') +sleeve=f'' +curtain+=sleeve +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen',530,2189,66,'Sanctification P052',500), + ('verse-1','a prophet since in Israel',530,2299,66,'Sanctification P052',500), + ('verse-2','like Moses, whom the LORD',530,2409,66,'Sanctification P052',500), + ('verse-3','knew face to face',530,2519,66,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',530,2640,48,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [200,2570,870,2690] if ident=='reference' else [95,2070,965,2560] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2056;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only semantic four-line typography refinement', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-and-complete-blue-red-arm-fold','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':110, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'vertical-text-prototype-v4' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('v4-v5-comparison',[('V4 — five-line verse',previous/'composed-preview-1000.png'),('V5 — semantic four-line verse',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,1300),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Lower, shallow tent textile',(0,1540,1160,2800),(20,65),(730,1300)), + ('Full arm fold; lower shard covered',(650,1510,1140,2240),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((im.width*2,im.height*2),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +d.text((845,1060),'Subtle linen fibers',font=label_font,fill='#f4ead4') +texture_strip=master.crop((80,1820,620,1910));texture_strip.thumbnail((520,180));detail.paste(texture_strip,(845,1100)) +detail.save(P/'panel-sleeve-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert 120000) +component_bounds=[int(xs.min()),int(ys.min()+600),int(xs.max()+1),int(ys.max()+601)] +fg_rgb=np.asarray(fg)[:,:,:3].astype(float) +component=(fa>0);component[:600]=False +red=component&(fg_rgb[:,:,0]>1.35*fg_rgb[:,:,2])&(fg_rgb[:,:,0]>1.25*fg_rgb[:,:,1])&(fg_rgb[:,:,0]>65) +blue=component&(fg_rgb[:,:,2]>1.4*fg_rgb[:,:,0])&(fg_rgb[:,:,2]>50) +assert np.count_nonzero(red)>1000 and np.count_nonzero(blue)>1000 +assert component_bounds[3]<=2030 +assert not np.any(fa[2040:2420,790:1040]) +assert np.array_equal(np.asarray(Image.open(P/'border-backing-preview.png')),np.asarray(Image.open(previous/'border-backing-preview.png'))) +report['structuralChecks']['sleeveComponentBounds']=component_bounds +report['structuralChecks']['redLiningPixels']=int(np.count_nonzero(red)) +report['structuralChecks']['blueSleevePixels']=int(np.count_nonzero(blue)) +report['structuralChecks']['excludedLowerRedShardForegroundPixels']=0 +report['structuralChecks']['backingPixelsIdenticalToV4']=True +report['template']['sleeveScope']='Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +# Three-way junction review makes both inclusion and exclusion explicit. +box=(650,1510,1150,2300) +board=Image.new('RGB',(1830,1120),'#11161c');d=ImageDraw.Draw(board) +source=Image.open(ART).convert('RGBA').crop(box) +selected=Image.alpha_composite(Image.new('RGBA',source.size,'#f4ead4'),fg.crop(box)) +composed=Image.open(P/'composed-preview-2000.png').convert('RGBA').crop(box) +for col,(label,im) in enumerate([('Source arm / descending shard',source),('Selected sleeve foreground',selected),('Backing covers lower shard',composed)]): + d.text((col*610+16,16),label,font=label_font,fill='#f4ead4') + im=im.resize((570,901),Image.Resampling.LANCZOS).convert('RGB');board.paste(im,(col*610+15,65)) + line_y=65+round((2030-box[1])*901/(box[3]-box[1])) + for x in range(col*610+15,col*610+585,18):d.line((x,line_y,x+9,line_y),fill='#dbac66',width=2) + d.text((col*610+16,995),'Dashed line: foreground ends above',font=label_font,fill='#d9c9a8') +board.save(P/'sleeve-selection-detail.png') + +# A 20px square clearance around the sleeve must contain no glyph ink. +local_text=(ta[1500:2100,650:1100]>0).astype(np.int32) +local_foreground=fa[1500:2100,650:1100]>0 +integral=np.pad(local_text,((21,20),(21,20))).cumsum(0).cumsum(1) +expanded=integral[41:,41:]-integral[:-41,41:]-integral[41:,:-41]+integral[:-41,:-41] +assert not np.any((expanded>0)&local_foreground) +report['structuralChecks']['sleeveGlyphClearanceAtLeastMasterPixels']=20 +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') + +# Typography-only regression: identical art/backing/sleeve/title/reference. +for filename in ['border-backing-preview.png','foreground-curtain.png','title-ink.png','reference-ink.png','without-curtain-2000.png']: + if filename=='without-curtain-2000.png': continue # This diagnostic still includes the changed verse. + assert np.array_equal(np.asarray(Image.open(P/filename)),np.asarray(Image.open(previous/filename))),filename +prior_face=np.asarray(Image.open(previous/'composed-preview-2000.png').convert('RGB')) +new_face=np.asarray(Image.open(P/'composed-preview-2000.png').convert('RGB')) +old_ink=np.asarray(Image.open(previous/'text-preview.png').convert('RGBA').getchannel('A'))>0 +new_ink=ta>0 +# Four-pixel allowance covers the compositor's 2x Lanczos text-edge support. +allowed=np.asarray(Image.fromarray(((old_ink|new_ink)*255).astype(np.uint8)).filter(ImageFilter.MaxFilter(9)))>0 +difference=np.any(prior_face!=new_face,axis=2) +assert not np.any(difference&~allowed) +report['structuralChecks']['sleevePixelsIdenticalToV4']=True +report['structuralChecks']['titleAndReferencePixelsIdenticalToV4']=True +report['structuralChecks']['changedPixelsOutsideTextSupport']=int(np.count_nonzero(difference&~allowed)) +report['structuralChecks']['onlyTypographyChanged']=True +report['typography']['requestedSemanticLines']=[r[1] for r in rows if r[0].startswith('verse-')] +report['typography']['usableBandTop']=upper +report['typography']['tracking']='Natural font spacing; no tracking compression or horizontal scaling' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +board=Image.new('RGB',(1440,740),'#11161c');d=ImageDraw.Draw(board) +for col,(label,folder) in enumerate([('V4 — previous line breaks',previous),('V5 — requested semantic lines',P)]): + d.text((col*720+16,15),label,font=label_font,fill='#f4ead4') + im=Image.open(folder/'composed-preview-2000.png').convert('RGB').crop((20,1760,1100,2740));im.thumbnail((690,640));board.paste(im,(col*720+15,65)) +board.save(P/'v4-v5-text-comparison.png') diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-1000.png new file mode 100644 index 0000000..45eb617 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-2000.png new file mode 100644 index 0000000..a0a9ff2 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-500.png new file mode 100644 index 0000000..3a7dff6 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview.svg new file mode 100644 index 0000000..321ae32 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/fonts.conf new file mode 100644 index 0000000..7dca305 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v5/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.png new file mode 100644 index 0000000..f1dfc90 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.svg new file mode 100644 index 0000000..d878b21 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/linen-texture-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/linen-texture-detail.png new file mode 100644 index 0000000..dd7eb6c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/linen-texture-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/panel-sleeve-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/panel-sleeve-junction-detail.png new file mode 100644 index 0000000..96482fa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/panel-sleeve-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/preview-validation.json new file mode 100644 index 0000000..ce62938 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/preview-validation.json @@ -0,0 +1,245 @@ +{ + "status": "passed", + "scope": "v05 Normal-only semantic four-line typography refinement", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-and-complete-blue-red-arm-fold", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 2026 + ], + "curtainFrameOrBackingOverlapPixels": 158854, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v4", + "lowerDepth": "Backing overlays Moses except for the complete folded arm garment. Separate descending red shard and all lower robe/tunic remain behind.", + "lowerTextLayout": "Four semantic 66px verse lines, natural tracking; original 48px reference unchanged; centered below complete sleeve by actual ink bounds", + "sleeveContour": "M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480", + "sleeveOcclusionFootprint": "M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z", + "textileGeometry": "Shallow pitched polygon; top peak lowered from y1320 to y1630, left start from y1500 to y1740", + "texture": "Low-opacity woven linen fibers; reinforced pitched seam and ties retained", + "sleeveScope": "Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it" + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen", + "baseline": 2189, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 172, + 2140, + 889, + 2191 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "a prophet since in Israel", + "baseline": 2299, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 185, + 2250, + 876, + 2318 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "like Moses, whom the LORD", + "baseline": 2409, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 112, + 2360, + 948, + 2420 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "knew face to face", + "baseline": 2519, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 282, + 2470, + 778, + 2521 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2640, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 253, + 2605, + 807, + 2654 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 112, + 2140, + 948, + 2521 + ], + "upperFlourishToVerseInk": 84, + "verseInkToReferenceLineBox": 84, + "centeringDifference": 0, + "lineBaselineGap": 110, + "requestedSemanticLines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ], + "usableBandTop": 2056, + "tracking": "Natural font spacing; no tracking compression or horizontal scaling" + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1200RowsIdenticalToPrior": true, + "selectedSleeveForegroundPixels": 41519, + "foregroundPixelsBelow2030": 0, + "lowerCloudForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "noFullRobeOrTunicForeground": true, + "sleeveComponentBounds": [ + 735, + 1689, + 977, + 2026 + ], + "redLiningPixels": 17962, + "blueSleevePixels": 4266, + "excludedLowerRedShardForegroundPixels": 0, + "backingPixelsIdenticalToV4": true, + "sleeveGlyphClearanceAtLeastMasterPixels": 20, + "sleevePixelsIdenticalToV4": true, + "titleAndReferencePixelsIdenticalToV4": true, + "changedPixelsOutsideTextSupport": 0, + "onlyTypographyChanged": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.png new file mode 100644 index 0000000..6966d29 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.svg new file mode 100644 index 0000000..021064c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/sleeve-selection-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/sleeve-selection-detail.png new file mode 100644 index 0000000..871973d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/sleeve-selection-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/source-composed-comparison.png new file mode 100644 index 0000000..43682b9 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.png new file mode 100644 index 0000000..beb4def Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.svg new file mode 100644 index 0000000..2aabf29 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-comparison.png new file mode 100644 index 0000000..8a399e5 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-text-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-text-comparison.png new file mode 100644 index 0000000..e389e34 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/v4-v5-text-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.png new file mode 100644 index 0000000..f52b86b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.svg new file mode 100644 index 0000000..9e696eb --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not arisen \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.png new file mode 100644 index 0000000..73f2bfd Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.svg new file mode 100644 index 0000000..88f8df0 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-1-ink.svg @@ -0,0 +1 @@ +a prophet since in Israel \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.png new file mode 100644 index 0000000..9591a68 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.svg new file mode 100644 index 0000000..c62ac2d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-2-ink.svg @@ -0,0 +1 @@ +like Moses, whom the LORD \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.png new file mode 100644 index 0000000..082e98a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.svg new file mode 100644 index 0000000..5c61040 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/verse-3-ink.svg @@ -0,0 +1 @@ +knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-1000.png new file mode 100644 index 0000000..d77089e Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-2000.png new file mode 100644 index 0000000..7e0ac0d Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-500.png new file mode 100644 index 0000000..1241af1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain.svg new file mode 100644 index 0000000..1e6f4ff --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v5/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/README.md new file mode 100644 index 0000000..8eeefd2 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/README.md @@ -0,0 +1,26 @@ +# Moses — provisional embroidered textile v6 + +Normal-only review candidate. The preserved v5 composition is the exact pixel base; this isolates one embroidery choice without changing the textile outline, sleeve, artwork, title, frame, verse or reference. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V5 / V6 comparison](v5-v6-comparison.png) +- [Enlarged embroidery](embroidery-detail.png) +- [Embroidery / sleeve / verse junction](embroidery-sleeve-junction-detail.png) +- [Editable embroidery](embroidery.svg) +- [Reproducible builder](build-preview.py) +- [Validation](preview-validation.json) + +Three sparse thread-line emblems show the burning bush, divided waters and two tablets. Muted old gold carries the symbols; one burgundy flame stitch and two lapis path stitches add restrained accents. A faint connecting stitch unifies the row. The emblems have no labels, enclosures or additional motifs. Rounded caps and short dashed paths retain a stitched rhythm; supersampling provides clean edges without blur. Static inspection covered 500px card size and enlarged detail. + +The four semantic verse lines remain at 66px Sanctification P052 Medium, with the same 84px balanced actual-ink spacing and unchanged single-line reference. The source art SHA256 remains `372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee`. + +Validation passes: all 15,826 changed master pixels are inside the embroidery alpha support; every non-embroidery master pixel is identical to v5. Text, sleeve and cloud-crown pixels are unchanged. The embroidery intersects neither foreground nor text, and all embroidery and glyph pixels sit on opaque backing. Exact content and shared font hashes pass. Smaller previews are downsampled from the verified master. + +Rebuild from the repository root: + +```sh +python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v6/build-preview.py +``` + +The builder intentionally references preserved v5 review assets as immutable comparison/base inputs and writes only inside this v6 folder. `composed-preview.svg` is an editable review wrapper over that exact base plus the vector embroidery; the PNG builder composites the supersampled embroidery onto the base to preserve all unaffected pixels precisely. No production printings, finish maps, harness assets or promotion were made. Pending user review. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/build-preview.py new file mode 100644 index 0000000..5e8d151 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/build-preview.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""Provisional embroidery-only Normal candidate, using the preserved v5 face.""" +from pathlib import Path +import hashlib,json,subprocess +import numpy as np +from PIL import Image,ImageDraw,ImageFont,ImageFilter +P=Path(__file__).resolve().parent +PRIOR=P.parent/'vertical-text-prototype-v5' +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/art-direction.md').is_file()) +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +ART=REV/'source/art-master.png' +source_hash=sha(ART) +prior_report=json.loads((PRIOR/'preview-validation.json').read_text()) +assert source_hash==prior_report['sourceArtwork']['sha256'] +manifest=json.loads((REPO/'fonts/manifest.json').read_text()) +for name in ['P052-Bold.otf','SanctificationP052-Medium.otf']: + assert sha(REPO/'fonts'/name)==manifest['files'][name]['sha256'] +# Three authored sparse thread-line emblems, not miniature stained-glass scenes. +# All artwork, geometry and typography are inherited pixel-for-pixel from v5. +head='' +embroidery=''' + + + + + + + + + + + + + + +''' +(P/'embroidery.svg').write_text(head+embroidery+'') +subprocess.run(['inkscape',str(P/'embroidery.svg'),'--export-type=png',f'--export-filename={P/"embroidery.png"}','--export-width=4000','--export-height=5600'],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.PIPE) +e=Image.open(P/'embroidery.png').convert('RGBA').convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA');e.save(P/'embroidery.png') +a=np.asarray(e.getchannel('A')) +fg=np.asarray(Image.open(PRIOR/'foreground-curtain.png').getchannel('A')) +txt=np.asarray(Image.open(PRIOR/'text-preview.png').getchannel('A')) +bg=np.asarray(Image.open(PRIOR/'border-backing-preview.png').getchannel('A')) +assert not np.any((a>0)&(fg>0)) +assert not np.any((a>0)&(txt>0)) +assert not np.any((fg>0)&(txt>0)) +assert np.all(bg[a>0]==255) and np.all(bg[txt>0]==255) +base=Image.open(PRIOR/'composed-preview-2000.png').convert('RGBA') +master=Image.alpha_composite(base,e).convert('RGB') +master.save(P/'composed-preview-2000.png') +for width in [1000,500]:master.resize((width,width*7//5),Image.Resampling.LANCZOS).save(P/f'composed-preview-{width}.png') +# Editable complete proof uses the exact approved prior face as its pixel base. +(P/'composed-preview.svg').write_text(head+''+embroidery+'') +diff=np.any(np.asarray(master)!=np.asarray(base.convert('RGB')),axis=2) +assert not np.any(diff&(a==0)) +assert np.array_equal(np.asarray(master)[txt>0],np.asarray(base.convert('RGB'))[txt>0]) +assert np.array_equal(np.asarray(master)[fg>0],np.asarray(base.convert('RGB'))[fg>0]) +checks=prior_report['typography']['checks'] +assert [c['text'] for c in checks if c['id'].startswith('verse-')]==['And there has not arisen','a prophet since in Israel','like Moses, whom the LORD','knew face to face'] +card=json.loads((REV/'card.json').read_text()) +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==card['excerpt'] +assert checks[-1]['text']==card['referenceDisplay'] +font=ImageFont.truetype(str(REPO/'fonts/P052-Bold.otf'),27) +board=Image.new('RGB',(1500,1100),'#15191e');draw=ImageDraw.Draw(board) +for i,(name,im) in enumerate([('V5 — plain canvas',base),('V6 — three stitched events',master)]): + draw.text((i*750+22,18),name,font=font,fill='#f4ead4');im=im.convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,64)) +board.save(P/'v5-v6-comparison.png') +master.crop((100,1780,730,2070)).resize((1260,580),Image.Resampling.LANCZOS).save(P/'embroidery-detail.png') +master.crop((20,1660,1080,2200)).save(P/'embroidery-sleeve-junction-detail.png') +report={'status':'passed','scope':'Provisional Normal-only embroidery addition','sourceArtwork':{'sha256':source_hash,'unchanged':sha(ART)==source_hash},'baseCandidate':str(PRIOR.relative_to(REPO)),'baseFaceSHA256':sha(PRIOR/'composed-preview-2000.png'),'embroidery':{'emblems':['burning bush','divided waters','two tablets'],'alphaBounds':list(e.getbbox()),'primaryThread':'#ab8b52 at .84 opacity','accents':['small burgundy flame stitch','two muted lapis water-path stitches'],'stitchWidthMasterPixels':6,'renderScale':2,'blur':False},'typography':prior_report['typography'],'structuralChecks':{'changedPixelsOutsideEmbroiderySupport':int(np.count_nonzero(diff&(a==0))),'changedPixelCount':int(diff.sum()),'textPixelsIdenticalToV5':True,'sleeveAndCrownPixelsIdenticalToV5':True,'allNonEmbroideryPixelsIdenticalToV5':True,'embroideryTextIntersectionPixels':0,'embroideryForegroundIntersectionPixels':0,'sleeveTextIntersectionPixels':0,'glyphPixelsOutsideOpaqueBacking':0,'embroideryPixelsOutsideOpaqueBacking':0,'sourceHashUnchanged':True,'fontHashes':'passed','exactContent':'passed'},'notPerformed':prior_report['notPerformed'],'review':'Static card-size and enlarged embroidery inspection; awaiting user choice.'} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +print(json.dumps(report['structuralChecks'],indent=2)) diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-1000.png new file mode 100644 index 0000000..e3bb334 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-2000.png new file mode 100644 index 0000000..2ddc24e Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-500.png new file mode 100644 index 0000000..b5dd3ff Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview.svg new file mode 100644 index 0000000..245e06d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/composed-preview.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-detail.png new file mode 100644 index 0000000..1bdb1a7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-sleeve-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-sleeve-junction-detail.png new file mode 100644 index 0000000..7891e8f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery-sleeve-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.png new file mode 100644 index 0000000..05b378a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.svg new file mode 100644 index 0000000..d6f946f --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/embroidery.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/preview-validation.json new file mode 100644 index 0000000..ecfbcee --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/preview-validation.json @@ -0,0 +1,210 @@ +{ + "status": "passed", + "scope": "Provisional Normal-only embroidery addition", + "sourceArtwork": { + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "baseCandidate": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v5", + "baseFaceSHA256": "0b4e25e55e552564725be30e876ce5b7a6b7683a587a3e4f11adcea2db33874f", + "embroidery": { + "emblems": [ + "burning bush", + "divided waters", + "two tablets" + ], + "alphaBounds": [ + 161, + 1857, + 669, + 1984 + ], + "primaryThread": "#ab8b52 at .84 opacity", + "accents": [ + "small burgundy flame stitch", + "two muted lapis water-path stitches" + ], + "stitchWidthMasterPixels": 6, + "renderScale": 2, + "blur": false + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen", + "baseline": 2189, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 172, + 2140, + 889, + 2191 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "a prophet since in Israel", + "baseline": 2299, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 185, + 2250, + 876, + 2318 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "like Moses, whom the LORD", + "baseline": 2409, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 112, + 2360, + 948, + 2420 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "knew face to face", + "baseline": 2519, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 282, + 2470, + 778, + 2521 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2640, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 253, + 2605, + 807, + 2654 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 112, + 2140, + 948, + 2521 + ], + "upperFlourishToVerseInk": 84, + "verseInkToReferenceLineBox": 84, + "centeringDifference": 0, + "lineBaselineGap": 110, + "requestedSemanticLines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ], + "usableBandTop": 2056, + "tracking": "Natural font spacing; no tracking compression or horizontal scaling" + }, + "structuralChecks": { + "changedPixelsOutsideEmbroiderySupport": 0, + "changedPixelCount": 15826, + "textPixelsIdenticalToV5": true, + "sleeveAndCrownPixelsIdenticalToV5": true, + "allNonEmbroideryPixelsIdenticalToV5": true, + "embroideryTextIntersectionPixels": 0, + "embroideryForegroundIntersectionPixels": 0, + "sleeveTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "embroideryPixelsOutsideOpaqueBacking": 0, + "sourceHashUnchanged": true, + "fontHashes": "passed", + "exactContent": "passed" + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "review": "Static card-size and enlarged embroidery inspection; awaiting user choice." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/v5-v6-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/v5-v6-comparison.png new file mode 100644 index 0000000..b7bf51f Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v6/v5-v6-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/README.md b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/README.md new file mode 100644 index 0000000..f6c3920 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/README.md @@ -0,0 +1,23 @@ +# Moses — shortened textile v7 + +Provisional Normal-only candidate based on v5. No embroidery is included. The pitched canvas top is lowered about 220–240 master pixels, exposing more original artwork and removing the unused upper canvas. Its reinforced seam and attachment loops follow the new shallow pitch. The complete blue/red arm fold remains source-registered above the seam, and the separate lower red shard remains covered. + +- [Full preview](composed-preview-1000.png) +- [Card-size preview](composed-preview-500.png) +- [V5 / V7 comparison](v5-v7-comparison.png) +- [Panel and sleeve detail](panel-sleeve-junction-detail.png) +- [Sleeve selection detail](sleeve-selection-detail.png) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +The verse retains the exact four semantic lines at 66px P052 Medium and unchanged positions. The 48px single-line reference, title, frame, source art, full sleeve layer, lower hem, linen fibers and linen color registration are preserved. All text remains on opaque backing, clear of the sleeve. No source art edits were made. + +The builder reconstructs the new top with native SVG paths. It then pins pixels outside the edited upper region (master x0–1110, y1590–2070) to v5, avoiding incidental clip-group antialias changes on the distant lower panel edge. The output PNG is the comparison authority; the editable full SVG retains vector reconstruction inputs. The only intended changes are the removed upper backing, relocated top seam/ties, and their recomposition against the original artwork and sleeve. + +Rebuild from the repository root: + +```sh +python3 in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/build-preview.py +``` + +Static review covers card size and enlarged panel/sleeve junction. Earlier candidates are preserved. No other printings, production finish masks, harness assets, acceptance or promotion were created. diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.png new file mode 100644 index 0000000..1f32684 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.svg new file mode 100644 index 0000000..6fe7c7a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/border-backing-preview.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/build-preview.py b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/build-preview.py new file mode 100644 index 0000000..061e79d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/build-preview.py @@ -0,0 +1,311 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont,ImageFilter + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +original_verse_path='M23 1740 L410 1630 L1090 1780 L1040 2745 H23Z' +verse_path='M23 1960 L410 1870 L1078.083 2010 L1040 2745 H23Z' +defs=defs.replace('',f'') +title_inner='' +# Reinforced pitched seam, construction stitches and a restrained woven hem. +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +defs=defs.replace('','''''') +# The tall textile is fixed to the outer left rail, not the background tent. +ties=''+''.join(f'' for y in [1960,2695])+'' +ties+='' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432' +lower_edge='' +cloud_low='' +cloud_top=top_edge+' L780 490 H0 V0Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# Frame occlusion is below the textile, not a lower foreground overlap. +# Preserve the existing curtain in front of the outer rail without putting it +# in front of the new lower backing or Moses. +defs=defs.replace('','') +curtain_frame_occlusion='' +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top'])+lead+'' +# The card/backing stays in front of Moses. Only this small blue sleeve tip +# crosses its upper-right seam; no lower robe, tunic or background is restored. +sleeve_edge='M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480' +sleeve_path=sleeve_edge+' L1120 1410 L1080 1330 L900 1190H650Z' +sleeve_footprint='M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z' +defs=defs.replace('',f'') +sleeve=f'' +curtain+=sleeve +# The continuous near post already belongs to v05 art. The registered source +# foreground retains it; no second geometry, gradient, or post overlay is drawn. +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen',530,2189,66,'Sanctification P052',500), + ('verse-1','a prophet since in Israel',530,2299,66,'Sanctification P052',500), + ('verse-2','like Moses, whom the LORD',530,2409,66,'Sanctification P052',500), + ('verse-3','knew face to face',530,2519,66,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',530,2640,48,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [200,2570,870,2690] if ident=='reference' else [95,2070,965,2560] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2056;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=2,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+curtain_frame_occlusion+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + # Pin the unchanged region to v5 pixels; only the top/seam is being prototyped. + # This also avoids harmless clip-group antialias differences on the far lower edge. + inherited=Image.open(P.parent/'vertical-text-prototype-v5'/(name+'-2000.png')).convert('RGB') + inherited.paste(master.crop((0,1590,1110,2070)),(0,1590)) + master=inherited + master.save(P/(name+'-2000.png')) + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'v05 Normal-only shortened textile top; no embroidery', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['source-art-with-background-tent-and-cloud','frame','source-curtain-occludes-outer-rail','opaque-vertical-backing','upper-nameplate-cloud-and-complete-blue-red-arm-fold','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':110, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Current source, prior horizontal layout, and the new vertical composition. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +previous=P.parent/'vertical-text-prototype-v5' +for name,items in [ + ('source-composed-comparison',[('Selected v05 source',ART),('Vertical textile prototype',P/'composed-preview-1000.png')]), + ('v5-v7-comparison',[('V5 — taller textile',previous/'composed-preview-1000.png'),('V7 — shortened textile',P/'composed-preview-1000.png')]), +]: + board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) + for i,(label,path) in enumerate(items): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) + board.save(P/(name+'.png')) +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1400,1300),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [ + ('Lower, shallow tent textile',(0,1540,1160,2800),(20,65),(730,1300)), + ('Full arm fold; lower shard covered',(650,1510,1140,2240),(845,65),(520,1250)), +]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im=im.resize((im.width*2,im.height*2),Image.Resampling.LANCZOS);im.thumbnail(size);detail.paste(im,origin) +d.text((845,1060),'Subtle linen fibers',font=label_font,fill='#f4ead4') +texture_strip=master.crop((80,2040,620,2110));texture_strip.thumbnail((520,180));detail.paste(texture_strip,(845,1100)) +detail.save(P/'panel-sleeve-junction-detail.png') +assert sha(ART)==json.loads((REV/'card.json').read_text())['artSHA256'] +assert 120000) +component_bounds=[int(xs.min()),int(ys.min()+600),int(xs.max()+1),int(ys.max()+601)] +fg_rgb=np.asarray(fg)[:,:,:3].astype(float) +component=(fa>0);component[:600]=False +red=component&(fg_rgb[:,:,0]>1.35*fg_rgb[:,:,2])&(fg_rgb[:,:,0]>1.25*fg_rgb[:,:,1])&(fg_rgb[:,:,0]>65) +blue=component&(fg_rgb[:,:,2]>1.4*fg_rgb[:,:,0])&(fg_rgb[:,:,2]>50) +assert np.count_nonzero(red)>1000 and np.count_nonzero(blue)>1000 +assert component_bounds[3]<=2030 +assert not np.any(fa[2040:2420,790:1040]) + +report['structuralChecks']['sleeveComponentBounds']=component_bounds +report['structuralChecks']['redLiningPixels']=int(np.count_nonzero(red)) +report['structuralChecks']['blueSleevePixels']=int(np.count_nonzero(blue)) +report['structuralChecks']['excludedLowerRedShardForegroundPixels']=0 +report['structuralChecks']['backingTopIntentionallyShortened']=True +report['template']['sleeveScope']='Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +# Three-way junction review makes both inclusion and exclusion explicit. +box=(650,1510,1150,2300) +board=Image.new('RGB',(1830,1120),'#11161c');d=ImageDraw.Draw(board) +source=Image.open(ART).convert('RGBA').crop(box) +selected=Image.alpha_composite(Image.new('RGBA',source.size,'#f4ead4'),fg.crop(box)) +composed=Image.open(P/'composed-preview-2000.png').convert('RGBA').crop(box) +for col,(label,im) in enumerate([('Source arm / descending shard',source),('Selected sleeve foreground',selected),('Backing covers lower shard',composed)]): + d.text((col*610+16,16),label,font=label_font,fill='#f4ead4') + im=im.resize((570,901),Image.Resampling.LANCZOS).convert('RGB');board.paste(im,(col*610+15,65)) + line_y=65+round((2030-box[1])*901/(box[3]-box[1])) + for x in range(col*610+15,col*610+585,18):d.line((x,line_y,x+9,line_y),fill='#dbac66',width=2) + d.text((col*610+16,995),'Dashed line: foreground ends above',font=label_font,fill='#d9c9a8') +board.save(P/'sleeve-selection-detail.png') + +# A 20px square clearance around the sleeve must contain no glyph ink. +local_text=(ta[1500:2100,650:1100]>0).astype(np.int32) +local_foreground=fa[1500:2100,650:1100]>0 +integral=np.pad(local_text,((21,20),(21,20))).cumsum(0).cumsum(1) +expanded=integral[41:,41:]-integral[:-41,41:]-integral[41:,:-41]+integral[:-41,:-41] +assert not np.any((expanded>0)&local_foreground) +report['structuralChecks']['sleeveGlyphClearanceAtLeastMasterPixels']=20 +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') + +# All typography and source-registered foreground remain identical to v5. +for filename in ['foreground-curtain.png','text-preview.png','title-ink.png','reference-ink.png']: + assert np.array_equal(np.asarray(Image.open(P/filename)),np.asarray(Image.open(previous/filename))),filename +prior_face=np.asarray(Image.open(previous/'composed-preview-2000.png').convert('RGB')) +new_face=np.asarray(Image.open(P/'composed-preview-2000.png').convert('RGB')) +difference=np.any(prior_face!=new_face,axis=2) +allowed=np.zeros(difference.shape,dtype=bool);allowed[1590:2070,:1110]=True +assert not np.any(difference&~allowed),np.count_nonzero(difference&~allowed) +assert np.array_equal(prior_face[ta>0],new_face[ta>0]) + +report['structuralChecks'].update({'textPixelsIdenticalToV5':True,'sleeveLayerIdenticalToV5':True,'changedPixelsOutsideBackingTopRegion':int(np.count_nonzero(difference&~allowed)),'changedPixelCount':int(difference.sum()),'noEmbroidery':True}) +report['typography']['requestedSemanticLines']=[r[1] for r in rows if r[0].startswith('verse-')] +report['typography']['usableBandTop']=upper +report['template']['preservedLinenRegistration']=True +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-1000.png new file mode 100644 index 0000000..9313243 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-2000.png new file mode 100644 index 0000000..841d0eb Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-500.png new file mode 100644 index 0000000..382506a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview.svg new file mode 100644 index 0000000..ce8a606 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/composed-preview.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/fonts.conf b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/fonts.conf new file mode 100644 index 0000000..832c2ae --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/font-cache \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.png new file mode 100644 index 0000000..f1dfc90 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.svg new file mode 100644 index 0000000..38e22f6 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/foreground-curtain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/linen-texture-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/linen-texture-detail.png new file mode 100644 index 0000000..42f957b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/linen-texture-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/panel-sleeve-junction-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/panel-sleeve-junction-detail.png new file mode 100644 index 0000000..967c5f7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/panel-sleeve-junction-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/preview-validation.json b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/preview-validation.json new file mode 100644 index 0000000..a9596bd --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/preview-validation.json @@ -0,0 +1,246 @@ +{ + "status": "passed", + "scope": "v05 Normal-only shortened textile top; no embroidery", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/source/art-master.png", + "sha256": "372fb8f9f5b398c943364a620c970fd81e000f3e0613f71eff84e087f6396bee", + "unchanged": true + }, + "template": { + "governingIdea": "Same foreground textile; complete folded blue sleeve and red inner lining form one V over its seam; lower red shard remains covered", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M23 1960 L410 1870 L1078.083 2010 L1040 2745 H23Z", + "compositionOrder": [ + "source-art-with-background-tent-and-cloud", + "frame", + "source-curtain-occludes-outer-rail", + "opaque-vertical-backing", + "upper-nameplate-cloud-and-complete-blue-red-arm-fold", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M1045 0 C1048 62 1012 102 971 105 C942 108 910 123 913 146 C916 166 952 176 967 209 C991 248 983 287 967 310 C956 325 971 347 959 371 C946 397 900 400 876 432 L780 490 H0 V0Z", + "cloudLowerContour": "", + "curtainAlphaBounds": [ + 0, + 0, + 1048, + 2026 + ], + "curtainFrameOrBackingOverlapPixels": 122876, + "layoutReference": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v5", + "lowerDepth": "Backing overlays Moses except for the complete folded arm garment. Separate descending red shard and all lower robe/tunic remain behind.", + "lowerTextLayout": "Four semantic 66px verse lines, natural tracking; original 48px reference unchanged; centered below complete sleeve by actual ink bounds", + "sleeveContour": "M670 1280 C650 1390 671 1540 725 1650 C756 1730 791 1820 781 1910 C777 1960 789 1998 808 2020 C829 1982 867 1942 893 1900 C942 1828 970 1746 1002 1666 C1031 1589 1066 1520 1086 1480", + "sleeveOcclusionFootprint": "M17 1736 L410 1618 L1102 1774 L1050 2753 H17Z", + "textileGeometry": "Shortened shallow pitch: left y1960, peak y1870, right y2010; original linen gradient coordinate registration retained", + "texture": "Low-opacity woven linen fibers; reinforced pitched seam and ties retained", + "sleeveScope": "Blue outer sleeve plus red inner lining and its integral cuff/edge glass, ending at the low V; no separate red shard below it", + "preservedLinenRegistration": true + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen", + "baseline": 2189, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 172, + 2140, + 889, + 2191 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "a prophet since in Israel", + "baseline": 2299, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 185, + 2250, + 876, + 2318 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "like Moses, whom the LORD", + "baseline": 2409, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 112, + 2360, + 948, + 2420 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "knew face to face", + "baseline": 2519, + "font": "Sanctification P052", + "weight": 500, + "size": 66, + "inkBounds": [ + 282, + 2470, + 778, + 2521 + ], + "safeBounds": [ + 95, + 2070, + 965, + 2560 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2640, + "font": "Sanctification P052", + "weight": 500, + "size": 48, + "inkBounds": [ + 253, + 2605, + 807, + 2654 + ], + "safeBounds": [ + 200, + 2570, + 870, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 112, + 2140, + 948, + 2521 + ], + "upperFlourishToVerseInk": 84, + "verseInkToReferenceLineBox": 84, + "centeringDifference": 0, + "lineBaselineGap": 110, + "requestedSemanticLines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ], + "usableBandTop": 2056 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "upper1200RowsIdenticalToPrior": true, + "selectedSleeveForegroundPixels": 41519, + "foregroundPixelsBelow2030": 0, + "lowerCloudForegroundPixels": 0, + "sourceMatchesV05CardSHA256": true, + "secondPostDrawn": false, + "noFullRobeOrTunicForeground": true, + "sleeveComponentBounds": [ + 735, + 1689, + 977, + 2026 + ], + "redLiningPixels": 17962, + "blueSleevePixels": 4266, + "excludedLowerRedShardForegroundPixels": 0, + "backingTopIntentionallyShortened": true, + "sleeveGlyphClearanceAtLeastMasterPixels": 20, + "textPixelsIdenticalToV5": true, + "sleeveLayerIdenticalToV5": true, + "changedPixelsOutsideBackingTopRegion": 0, + "changedPixelCount": 248823, + "noEmbroidery": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Provisional Normal-only prototype; static inspection at card size and enlarged junctions; pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false + }, + "foregroundAdaptation": "Complete source-registered blue sleeve and red inner lining restore the coherent arm fold down to its V near y2020 and back toward the wrist. The separate lower red shard, tunic, lower robe and background remain behind the backing." +} diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.png new file mode 100644 index 0000000..6966d29 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.svg new file mode 100644 index 0000000..021064c --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/reference-ink.svg @@ -0,0 +1 @@ +Deuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/sleeve-selection-detail.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/sleeve-selection-detail.png new file mode 100644 index 0000000..c9b6299 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/sleeve-selection-detail.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/source-composed-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/source-composed-comparison.png new file mode 100644 index 0000000..6b2da7c Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/source-composed-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.png new file mode 100644 index 0000000..beb4def Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.svg new file mode 100644 index 0000000..2aabf29 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/text-preview.svg @@ -0,0 +1 @@ +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.png new file mode 100644 index 0000000..4512dd1 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.svg new file mode 100644 index 0000000..6072c1a --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/title-ink.svg @@ -0,0 +1 @@ +MOSES \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/v5-v7-comparison.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/v5-v7-comparison.png new file mode 100644 index 0000000..7fdecd5 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/v5-v7-comparison.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.png new file mode 100644 index 0000000..f52b86b Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.svg new file mode 100644 index 0000000..9e696eb --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-0-ink.svg @@ -0,0 +1 @@ +And there has not arisen \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.png new file mode 100644 index 0000000..73f2bfd Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.svg new file mode 100644 index 0000000..88f8df0 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-1-ink.svg @@ -0,0 +1 @@ +a prophet since in Israel \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.png new file mode 100644 index 0000000..9591a68 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.svg new file mode 100644 index 0000000..c62ac2d --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-2-ink.svg @@ -0,0 +1 @@ +like Moses, whom the LORD \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.png new file mode 100644 index 0000000..082e98a Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.svg new file mode 100644 index 0000000..5c61040 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/verse-3-ink.svg @@ -0,0 +1 @@ +knew face to face \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-1000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-1000.png new file mode 100644 index 0000000..9df9367 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-1000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-2000.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-2000.png new file mode 100644 index 0000000..3bed2e7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-2000.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-500.png b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-500.png new file mode 100644 index 0000000..58ff5d7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain-500.png differ diff --git a/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain.svg b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain.svg new file mode 100644 index 0000000..7f71c41 --- /dev/null +++ b/artifacts/cards/BP-001-moses/review/vertical-text-prototype-v7/without-curtain.svg @@ -0,0 +1,7 @@ + + + + + + +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/art-master.png b/artifacts/cards/BP-001-moses/source/art-master.png new file mode 100644 index 0000000..602d386 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/art-master.png differ diff --git a/artifacts/cards/BP-001-moses/source/base-v04.png b/artifacts/cards/BP-001-moses/source/base-v04.png new file mode 100644 index 0000000..18fbcb6 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/base-v04.png differ diff --git a/artifacts/cards/BP-001-moses/source/build-art.py b/artifacts/cards/BP-001-moses/source/build-art.py new file mode 100644 index 0000000..3bb69c6 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/build-art.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +"""Deterministic narrow post extension; original pixels untouched outside alpha.""" +from pathlib import Path +from PIL import Image,ImageDraw,ImageFilter +import numpy as np,json,hashlib,shutil +p=Path(__file__).resolve().parent.parent +base=Image.open(p/'source/base-v04.png').convert('RGB');W,H=base.size +# Master coordinates: begin inside the final existing segment, terminate at socket. +box=(310,994,379,2652);s=4;x0,y0,x1,y1=box +layer=Image.new('RGBA',((x1-x0)*s,(y1-y0)*s));d=ImageDraw.Draw(layer) +def left(y):return 332.5-(y-975)*12.5/(2645-975) +def pt(x,y):return ((x-x0)*s,(y-y0)*s) +def poly(points,fill):d.polygon([pt(x,y) for x,y in points],fill=fill) +# Dark structural silhouette overlays the cloud, with a small shaped ground end. +poly([(left(994),994),(left(994)+42,994),(left(2626)+40,2626),(left(2645)+35,2645),(left(2645)+1,2640)],'#19190f') +# Unequal segment heights mirror the extant near and far posts. +edges=[994,1200,1417,1601,1818,2040,2259,2441,2628] +for i,(a,b) in enumerate(zip(edges,edges[1:])): + top=a+6 if i else a;bottom=b-7 + l1,l2=left(top)+9,left(bottom)+9;wid=23 + poly([(l1,top),(l1+wid,top+1),(l2+wid,bottom),(l2,bottom-1)],'#c08b28') + poly([(l1+3,top),(l1+wid-3,top+1),(l2+wid-3,bottom),(l2+3,bottom-1)],'#f0ca51') + poly([(l1+4,top+2),(l1+9,top+2),(l2+9,bottom-2),(l2+4,bottom-2)],'#f9e591') + # Inset diagonal pane break in selected longer segments, never across the outline. + if i in [2,5,7]: + mid=(top+bottom)/2;lx=left(mid)+9 + d.line([pt(lx,mid-10),pt(lx+23,mid+11)],fill='#8b6726',width=2*s) +# Contained worked-glass variation sampled from the original source, not fresh noise. +arr=np.array(layer);gold=(arr[:,:,3]>0)&(arr[:,:,0]>100) +tex=base.crop((510,600,610,850)).resize(layer.size,Image.Resampling.BICUBIC) +t=np.asarray(tex,dtype=float).mean(axis=2);variation=np.clip((t-t.mean())*.26,-17,17) +rgb=arr[:,:,:3].astype(float);rgb[gold]+=variation[gold,None];arr[:,:,:3]=np.clip(rgb,0,255).astype('uint8') +layer=Image.fromarray(arr).filter(ImageFilter.GaussianBlur(1.6)).resize((x1-x0,y1-y0),Image.Resampling.LANCZOS) +overlay=Image.new('RGBA',(W,H));overlay.paste(layer,(x0,y0));overlay.save(p/'source/post-extension.png') +art=Image.alpha_composite(base.convert('RGBA'),overlay).convert('RGB');art.save(p/'source/art-master.png') +for name,size in [('art-review.png',(1000,1400)),('art-card-size.png',(500,700)),('art-thumbnail.png',(250,350))]:art.resize(size,Image.Resampling.LANCZOS).save(p/'review'/name) +board=Image.new('RGB',(720,1020),'#ece5d5');dd=ImageDraw.Draw(board) +for j,(label,im) in enumerate([('v04 — interrupted',base),('v05 — continued',art)]): + dd.text((j*360+15,10),label,fill='#263f50') + detail=im.crop((265,870,440,2680));detail.thumbnail((340,960));board.paste(detail,(j*360+100,40)) +board.save(p/'review/post-before-after.png') +join=Image.new('RGB',(840,660),'#ece5d5');dd=ImageDraw.Draw(join) +for j,(label,im) in enumerate([('v04',base),('v05',art)]): + dd.text((j*420+10,10),label,fill='#263f50') + for k,b in enumerate([(305,940,395,1085),(300,2490,390,2670)]): + crop=im.crop(b);crop.thumbnail((380,300));crop=crop.resize((180,290 if k==0 else 360));join.paste(crop,(j*420+110,35+k*290)) +join.save(p/'review/post-join-detail.png') +sha=lambda f:hashlib.sha256(f.read_bytes()).hexdigest() +a=np.asarray(base);b=np.asarray(art);diff=np.any(a!=b,axis=2);ys,xs=np.where(diff) +assert not diff[:y0].any() and not diff[y1:].any() and not diff[:,:x0].any() and not diff[:,x1:].any() +report={'status':'pass','baseSHA256':sha(p/'source/base-v04.png'),'artSHA256':sha(p/'source/art-master.png'),'dimensions':[W,H],'changedPixels':int(diff.sum()),'changedFraction':float(diff.mean()),'differenceBoundsExclusive':[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)],'authorizedRegion':list(box),'allPixelsOutsidePostRegionIdentical':True,'method':'Deterministic authored overlay, antialiased at 4x; RGB compositing only inside overlay alpha','scope':'Art-only, no card assembly'} +(p/'review/source-validation.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps(report)) diff --git a/artifacts/cards/BP-001-moses/source/foreground/cloud-crown-over-frame-2000.png b/artifacts/cards/BP-001-moses/source/foreground/cloud-crown-over-frame-2000.png new file mode 100644 index 0000000..1d74d55 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/foreground/cloud-crown-over-frame-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/foreground/curtain-over-rail-2000.png b/artifacts/cards/BP-001-moses/source/foreground/curtain-over-rail-2000.png new file mode 100644 index 0000000..70436cc Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/foreground/curtain-over-rail-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/foreground/foreground-2000.png b/artifacts/cards/BP-001-moses/source/foreground/foreground-2000.png new file mode 100644 index 0000000..f1dfc90 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/foreground/foreground-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/foreground/selected-foreground.svg b/artifacts/cards/BP-001-moses/source/foreground/selected-foreground.svg new file mode 100644 index 0000000..45c5de5 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/foreground/selected-foreground.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/foreground/textless-frame-occlusion-2000.png b/artifacts/cards/BP-001-moses/source/foreground/textless-frame-occlusion-2000.png new file mode 100644 index 0000000..100ec64 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/foreground/textless-frame-occlusion-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/backing-2000.png b/artifacts/cards/BP-001-moses/source/layers/backing-2000.png new file mode 100644 index 0000000..78ecd60 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/layers/backing-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/backing-borderless-2000.png b/artifacts/cards/BP-001-moses/source/layers/backing-borderless-2000.png new file mode 100644 index 0000000..8ab8542 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/layers/backing-borderless-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/backing.svg b/artifacts/cards/BP-001-moses/source/layers/backing.svg new file mode 100644 index 0000000..6aff236 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/layers/backing.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/layers/frame-2000.png b/artifacts/cards/BP-001-moses/source/layers/frame-2000.png new file mode 100644 index 0000000..b9571fc Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/layers/frame-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/frame-textless-2000.png b/artifacts/cards/BP-001-moses/source/layers/frame-textless-2000.png new file mode 100644 index 0000000..153cb61 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/layers/frame-textless-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/frame.svg b/artifacts/cards/BP-001-moses/source/layers/frame.svg new file mode 100644 index 0000000..0155f82 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/layers/frame.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/layers/layout.json b/artifacts/cards/BP-001-moses/source/layers/layout.json new file mode 100644 index 0000000..897ef2b --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/layers/layout.json @@ -0,0 +1,44 @@ +{ + "id": "BP-001-moses-legendary-tent-textile-v1", + "canvas": [ + 2000, + 2800 + ], + "selectedProof": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7", + "titleBacking": "upper-right paper plaque beneath the source cloud crown", + "verseBacking": "shortened left tent textile with shallow pitched seam", + "depthOrder": [ + "base-art", + "frame", + "curtain-over-rail", + "backings", + "cloud-crown-and-complete-blue-red-sleeve", + "text" + ], + "printingComponents": { + "normal": [ + "base-art", + "frame", + "curtain-over-rail", + "backing", + "adapted-foreground", + "text" + ], + "boundless": [ + "base-art" + ], + "borderless": [ + "base-art", + "backing", + "adapted-foreground", + "text" + ], + "textless": [ + "base-art", + "frame", + "curtain-over-rail", + "cloud-crown-over-frame" + ] + }, + "foregroundScope": "Normal and Borderless restore the source cloud crown plus complete blue outer sleeve and red inner lining ending at the low V. Textless restores the tent/cloud crown over the top-left rail. The separate lower red shard stays behind the backing." +} diff --git a/artifacts/cards/BP-001-moses/source/layers/overlay-normal-2000.png b/artifacts/cards/BP-001-moses/source/layers/overlay-normal-2000.png new file mode 100644 index 0000000..23b4768 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/layers/overlay-normal-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/layers/selected-overlay.svg b/artifacts/cards/BP-001-moses/source/layers/selected-overlay.svg new file mode 100644 index 0000000..6fe7c7a --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/layers/selected-overlay.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/material/base-pane-coverage.png b/artifacts/cards/BP-001-moses/source/material/base-pane-coverage.png new file mode 100644 index 0000000..93e026c Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/material/base-pane-coverage.png differ diff --git a/artifacts/cards/BP-001-moses/source/material/base-raw-ridges.png b/artifacts/cards/BP-001-moses/source/material/base-raw-ridges.png new file mode 100644 index 0000000..c8f01fc Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/material/base-raw-ridges.png differ diff --git a/artifacts/cards/BP-001-moses/source/material/foreground-analysis-plate.png b/artifacts/cards/BP-001-moses/source/material/foreground-analysis-plate.png new file mode 100644 index 0000000..29d98a5 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/material/foreground-analysis-plate.png differ diff --git a/artifacts/cards/BP-001-moses/source/material/foreground-pane-coverage.png b/artifacts/cards/BP-001-moses/source/material/foreground-pane-coverage.png new file mode 100644 index 0000000..1ce0480 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/material/foreground-pane-coverage.png differ diff --git a/artifacts/cards/BP-001-moses/source/material/foreground-raw-ridges.png b/artifacts/cards/BP-001-moses/source/material/foreground-raw-ridges.png new file mode 100644 index 0000000..01213b7 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/material/foreground-raw-ridges.png differ diff --git a/artifacts/cards/BP-001-moses/source/post-extension.png b/artifacts/cards/BP-001-moses/source/post-extension.png new file mode 100644 index 0000000..f325021 Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/post-extension.png differ diff --git a/artifacts/cards/BP-001-moses/source/prior-revision-hashes.json b/artifacts/cards/BP-001-moses/source/prior-revision-hashes.json new file mode 100644 index 0000000..c0f7afe --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/prior-revision-hashes.json @@ -0,0 +1,23 @@ +{ + "README.md": "311a75b80ae042313a8479d295715ea6a1d12406964aad89537dc0b1d1ce0e1f", + "card.json": "e10b36169c7e5c682c5e8741ee05340e8250fa3ff5d961f53546a28eb9fbc1f7", + "high/manifest.json": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a", + "low/manifest.json": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b", + "manifest.json": "c054d46d8ac354ee24170b42301e3698056a70f385be2cb2386e6f2f461fc78a", + "med/manifest.json": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6", + "review/art-card-size.png": "d90c69285530282d7f72ad60ae5fb2b02b2eb130dfc85738e19a00daafb50f43", + "review/art-review.png": "931f22f0883c56fd3a87540864e3c79858fde87cd0ae30dcc7955d9940282445", + "review/art-thumbnail.png": "1d65a13ce32a5100b36e0475b920f8f78bafda7097807603e459d5502764ec1d", + "review/illustration-review.md": "e79bf7b260142fbb767d85b1389e4111fd33c65010883b513409efff4db4a358", + "review/source-validation.json": "ae30dc558fe34aa8cdd88f90f23915ae5cfc78f091e8ae0c53493e0290264476", + "source/art-fit.json": "c945ae1717313d1f83f94e7ddd563c05c63529fe3b0dd0d4e76f4d99f0bf2d0f", + "source/art-master.png": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e", + "source/art-native.png": "36f6b3ab7360b7e3d8eaf93ce1dd9edd259dfc77424ca1ef4ac729cfa126ade6", + "source/edit-prompt.txt": "bd590461332bf9afec442d46ebef829978a64fd51c1a76074b140bd177cd56d9", + "source/edit-reference.jpg": "29abc4ff23b6badc4a321b067735a4bed2eb31666759e4d12a35e4bdde69fcaa", + "source/fit-art.py": "ae5418915f2bcfb464714b1898a9fdfa86e4117f2de226a01260be1fd841c611", + "source/generation.json": "f6b2c5d8158aa8ef03544cf61eae75e4f4f120bf7f330e2b813f014a612a2c2a", + "source/prior-revision-hashes.json": "435f9b4a7af82e53bec288fc74f7853e2cf4039b3509e4f5a6fcbf9e4a0df9b8", + "source/refinement.md": "685f8e1f1875caa43fa9d1267835d096b02aa83ced550a28e8fa387fc3cd2c2e", + "source/validate-art.py": "d6ccbe387f1dc91eac5b4c1753cdc0d475f34527530a915c68c6905dc07bd6a3" +} diff --git a/artifacts/cards/BP-001-moses/source/production-inputs.json b/artifacts/cards/BP-001-moses/source/production-inputs.json new file mode 100644 index 0000000..3895d94 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/production-inputs.json @@ -0,0 +1,38 @@ +{ + "selectedProof": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7", + "files": { + "normal": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/composed-preview-2000.png", + "sha256": "0bef4f96859790f5c42bc782a26f45a0835134338f35feaccbd60d4827c5d14d" + }, + "overlaySvg": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/border-backing-preview.svg", + "sha256": "c2ca725ac8821a62d8af8176cd25ecf5e4a9c317dc62947cc8242a77c8f53e00" + }, + "foreground": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/foreground-curtain.png", + "sha256": "094ec94400a21dd78f8e237104ebf11bf8cafc868f27c1ef78b640b7b3d5f857" + }, + "foregroundSvg": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/foreground-curtain.svg", + "sha256": "2b2947ad99c922fa56becbbb1a1e8a8aec5f9cffcaacc8e63f1ec40ae3c164cc" + }, + "text": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/text-preview.png", + "sha256": "2623f00ffe8d9e5883215e08452f2ecd51eb21bb55ab79f485ce6ca82f1e1155" + }, + "textSvg": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/text-preview.svg", + "sha256": "57c51e06cfb8732f1500311936f95b4f4e0255af35b0218341f1126ce8370741" + }, + "validation": { + "path": "in-progress/cards/BP-001-moses/revisions/v05/review/vertical-text-prototype-v7/preview-validation.json", + "sha256": "e5fe7a009d03ecc33ee6a92b1c357f78992b39e88e58c51079eb5675546c7772" + } + }, + "fonts": { + "P052-Bold.otf": "ba6503baaf0f9e4e40a69cc4c0e57049fbde313fe183aba8fe823208337a94af", + "SanctificationP052-Medium.otf": "1e0e1f7f4a5b2899374de51e40f15446a04a5c0163d6a1943155f964c1ace1fb" + }, + "finishRecipeSHA256": "23331ce56a717edb4e6f85956d11fdd98397009c4e6dd230d8c334c77571dd18" +} diff --git a/artifacts/cards/BP-001-moses/source/provenance.json b/artifacts/cards/BP-001-moses/source/provenance.json new file mode 100644 index 0000000..401bc52 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/provenance.json @@ -0,0 +1,27 @@ +{ + "method": "Deterministic authored raster overlay; no image generation", + "artDirectionModel": "gpt-6-astra", + "artDirectionReasoning": "medium", + "base": "base-v04.png", + "baseOrigin": "../../v04/source/art-master.png", + "baseSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e", + "authoringSource": "build-art.py", + "overlay": "post-extension.png", + "result": "art-master.png", + "request": "Continue the near/left golden entrance post down through the cloud to its ground socket. Preserve all other pixels.", + "reference": "v03/review/layout-preview-astra-v5/build-preview.py near-entrance-post geometry reviewed; redrawn narrowly from existing v04 upper segment rather than replacing entire post.", + "transform": { + "canvas": [ + 2000, + 2800 + ], + "baseScale": 1, + "baseOffset": [ + 0, + 0 + ], + "crop": null + }, + "validation": "../review/source-validation.json", + "approval": null +} diff --git a/artifacts/cards/BP-001-moses/source/selected-normal-2000.png b/artifacts/cards/BP-001-moses/source/selected-normal-2000.png new file mode 100644 index 0000000..841d0eb Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/selected-normal-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/text/selected-text.svg b/artifacts/cards/BP-001-moses/source/text/selected-text.svg new file mode 100644 index 0000000..2aabf29 --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/text/selected-text.svg @@ -0,0 +1 @@ +MOSESAnd there has not arisena prophet since in Israellike Moses, whom the LORDknew face to faceDeuteronomy 34:10 • ESV \ No newline at end of file diff --git a/artifacts/cards/BP-001-moses/source/text/text-2000.png b/artifacts/cards/BP-001-moses/source/text/text-2000.png new file mode 100644 index 0000000..beb4def Binary files /dev/null and b/artifacts/cards/BP-001-moses/source/text/text-2000.png differ diff --git a/artifacts/cards/BP-001-moses/source/typography-layout.json b/artifacts/cards/BP-001-moses/source/typography-layout.json new file mode 100644 index 0000000..7ac9f8e --- /dev/null +++ b/artifacts/cards/BP-001-moses/source/typography-layout.json @@ -0,0 +1,42 @@ +{ + "canvas": [ + 2000, + 2800 + ], + "policy": "Bespoke Legendary coordinates; semantic four-line verse; actual ink centered in the reserved band.", + "titleAnchor": [ + 1458, + 244 + ], + "verseAnchorX": 530, + "verseBaselines": [ + 2189, + 2299, + 2409, + 2519 + ], + "referenceBaseline": 2640, + "fontSizes": { + "title": 142, + "verse": 66, + "reference": 48 + }, + "verseLines": [ + "And there has not arisen", + "a prophet since in Israel", + "like Moses, whom the LORD", + "knew face to face" + ], + "reference": "Deuteronomy 34:10 \u2022 ESV", + "measured": { + "verseInkUnion": [ + 112, + 2140, + 948, + 2521 + ], + "upperFlourishToVerseInk": 84, + "verseInkToReferenceLineBox": 84, + "centeringDifference": 0 + } +} diff --git a/docs/art-direction.md b/docs/art-direction.md index 60757ae..527fe09 100644 --- a/docs/art-direction.md +++ b/docs/art-direction.md @@ -297,9 +297,31 @@ The proposed template coordinates, validation checks, and GIMP/SVG tooling workf Each Legendary artwork variant may have its own frame, backing shapes, panel positions, ornament, and illustrated overlaps. These components need not match other Legendaries or follow the Common–Extraordinary tier templates. Begin with one governing visual idea and let the layout support its subject, space, and emotional impression. Legendary presence may come from exceptional beauty, light, intimacy, or scale; ornament and overlaps are optional. -Selected illustrated elements may cross the frame or backing to integrate the subject with the card. Author them separately above the border/backing system and below the lettering. Adapt their shape, folds, silhouette, or placement when this improves the impression; a foreground version need not reproduce the artwork concealed beneath the backing exactly. Preserve visible continuity into the scene, coherent glass construction and lighting, and believable anatomy or material behavior. Do not make a backing notch or narrow revealed strip stand in for a convincing complete form. +### Depth and Card Hierarchy -Plan panel coverage and foreground clearance together. Use a provisional composed preview with real shared P052 lettering before investing in finished ornament. Inspect the whole card, the element's join and hem/edge, and delivery-size text clearance. Keep all lettering on a legible field and free of foreground intersections. Legendary palette exceptions remain documented template decisions; the shared font roles still apply. +Define the intended depth before authoring the template: **base environment → card frame/backing → selected illustrated foreground → deterministic lettering**. Identify which parts of the scene are background, which form the focal subject, and which complete element—if any—may cross above the card structure. The frame and backing remain the primary product layer; an illustrated overlap is a controlled exception. + +An environmental element that sits behind or beside the subject in the illustration must not be moved above the backing when that reversal makes it appear to pass in front of the subject. Smoother extraction cannot repair contradictory depth. Redesign the backing placement or choose an element that is genuinely in the foreground. A title plaque and supporting-text backing may use different shapes and interaction strengths when the scene benefits from it. + +### Backing Placement and Restraint + +Use the illustration's negative space instead of assuming a full-width title or footer. A Legendary backing may be vertical, asymmetric, pitched, suspended, or otherwise scene-specific, provided it keeps the focal face and gesture readable and retains a coherent card silhouette. Plan the backing region with the illustration rather than treating it as a late overlay. + +Size the backing to its content and measured breathing room. When unused backing area feels accidental, first shorten, reshape, or reposition the backing. Add symbolism or ornament only when it supports the governing idea and remains subordinate at card size. Legendary distinction does not require filling every quiet area. + +### Illustrated Foregrounds + +Selected illustrated elements may cross the frame or backing when they strengthen depth, silhouette, or the governing idea. Author them separately above the frame/backing and below the lettering. Adapt their folds, silhouette, or placement when needed; a foreground version need not reproduce artwork concealed by the backing pixel for pixel. + +Select a complete logical object or material form rather than a convenient color patch. A folded garment may include its differently colored lining, cuff, or connected return; detached robe panels and unrelated shards remain behind the backing. Preserve visible continuity into the source scene, coherent glass construction and lighting, believable anatomy or material behavior, and a smooth intentional edge. A backing notch, narrow revealed strip, connected pixel corridor, or technically valid alpha mask does not establish a convincing form by itself. + +### Typography and Review + +Use real shared P052 lettering in the first composed preview. Choose line breaks by natural phrase and sentence structure before reducing font size; avoid breaks that create false pauses or separate dependent phrases. Measure the combined visible ink rather than centering nominal text boxes. For an asymmetric backing, define the usable field after foreground obstructions and center the verse within that field while retaining the fixed reference region and collision clearance. + +Inspect the whole card, the foreground join, complete silhouette, backing proportions, and text rhythm at enlarged, standard, and thumbnail sizes. Keep all lettering on a legible field and free of foreground intersections. Legendary palette exceptions remain documented template decisions; the shared font roles still apply. + +Prototype and approve the **Normal composition first**. Do not derive Borderless, Textless, Boundless, finish masks, or harness fixtures until the Normal frame, backing, foreground selection, and typography are accepted. This prevents discarded experiments from producing misleading or stale derivatives. For backing-specific overlaps, Normal and Borderless use the selected adapted foreground. Textless and Boundless reveal the selected full-bleed illustration without adding that foreground a second time. If an adaptation should appear in every printing, incorporate it into an explicitly revised artwork variant. Record the chosen components for each printing. diff --git a/docs/card-art-brief.md b/docs/card-art-brief.md index a78af07..8c47277 100644 --- a/docs/card-art-brief.md +++ b/docs/card-art-brief.md @@ -70,11 +70,15 @@ Prestigious, with richer environments, selective symbolism, and greater scale or Bespoke, reverent, iconic; one governing narrative or visual idea. Exceptional presence may come from an epic scene or spacious contemplation. Use remarkable light, deliberate symbolism, and a cohesive jewel palette with tasteful gold where appropriate. Within-segment modeling can be especially refined, but restraint remains available. A Legendary may have fewer elements or segments than an Extraordinary. Different artwork variants may explore different governing ideas. -A Legendary may be composed with selected illustrated foreground elements crossing its frame or text backing. This is optional and should strengthen the governing idea, depth, or silhouette. A cloak, branch, hand, or architectural element must remain visibly connected to its source in the scene and share the same glass construction, palette, light, and worked-glass surface. +Before generation, the card-specific brief should name the intended depth planes: background environment, focal subject, likely card-backing region, and any complete element that could plausibly become a foreground overlap. Reserve useful negative space for the proposed backing while keeping the full-bleed scene intentional. A background cloud, structure, or landscape feature remains behind the subject even when its silhouette would be attractive over a panel. -The foreground version may be redrawn, regenerated, reshaped, or repositioned to improve the composed card. It need not match the covered base artwork pixel for pixel. Preserve recognizable identity and believable form, including the visible join into the scene; shaping an element for an overlap is an artistic decision rather than a requirement to reproduce its original resting edge. Avoid detached tips, jagged extraction edges, missing dark seams, and flat cutoffs inherited from a surface that is now hidden. A connected pixel corridor alone cannot establish visual continuity. +A Legendary may be composed with selected illustrated foreground elements crossing its frame or text backing. This is optional and should strengthen the governing idea, depth, or silhouette. Choose an element that is spatially in front in the depicted scene, such as a cloak, sleeve, hand, branch, or near architectural feature. It must remain visibly connected to its source and share the same glass construction, palette, light, and worked-glass surface. -Keep lettering clear and legible, and preserve the complete full-bleed illustration for printings that reveal it. Review any adapted foreground in a specifically requested composed preview, including the join and silhouette at enlarged and normal viewing sizes. [Legendary bespoke composition](./art-direction.md#legendary-bespoke-composition) owns panel/frame presentation; [Legendary foreground composition](./card-layer-pipeline.md#legendary-foreground-composition) owns retained sources, registration, and per-printing assembly. +The foreground version may be redrawn, regenerated, reshaped, or repositioned to improve the composed card. It need not match the covered base artwork pixel for pixel. Preserve recognizable identity and the complete logical form: include connected linings, cuffs, folds, or material returns even when their colors differ, and exclude detached shards belonging to another garment panel or depth plane. Avoid detached tips, jagged extraction edges, missing dark seams, and flat cutoffs inherited from a surface that is now hidden. A connected pixel corridor alone cannot establish visual continuity. + +Correct illustration problems in the illustration before compositing. Ambiguous materials, broken architecture, unclear silhouettes, or inappropriate realism should receive a new art revision rather than being concealed with a frame, backing, or finish mask. + +Keep lettering clear and legible, and preserve the complete full-bleed illustration for printings that reveal it. Review any adapted foreground in a specifically requested Normal preview, including the join and silhouette at enlarged and normal viewing sizes. [Legendary bespoke composition](./art-direction.md#legendary-bespoke-composition) owns panel/frame presentation; [Legendary foreground composition](./card-layer-pipeline.md#legendary-foreground-composition) owns retained sources, registration, and per-printing assembly. ## Illustration Review diff --git a/docs/card-layer-pipeline.md b/docs/card-layer-pipeline.md index 3fdf89f..9aaa1df 100644 --- a/docs/card-layer-pipeline.md +++ b/docs/card-layer-pipeline.md @@ -79,11 +79,17 @@ Validate required opaque panels/perimeters and intended clear regions separately Follow [Legendary bespoke composition](./art-direction.md#legendary-bespoke-composition). Keep the complete base illustration separate from an optional adapted foreground element. Retain each foreground's authored silhouette/alpha and editable source; for generated or edited donors, also retain the exact prompt, native output, recorded fit/placement, and hashes. The foreground may intentionally differ from the art behind the backing. Record that difference instead of requiring source-RGB identity for the adapted region. -Place all fitted components on the same 2000 × 2800 canvas with explicit transforms. The usual order is base illustration → selected frame/backing overlay → illustrated foreground → deterministic lettering. Record any template-specific change in order. Use the same source, transform, alpha convention, and resampling for the face and associated finish data. +Before drawing masks, record a depth plan for the candidate: background environment, focal subject, frame/backing, selected foreground exception, and lettering. The usual full-canvas order is base illustration → selected frame/backing → selected illustrated foreground → deterministic lettering. A template may use another documented order, but it must preserve the scene's spatial relationships. Reject a foreground treatment that makes a background element appear to pass in front of the focal subject. -Choose a silhouette that preserves the complete intended form, internal leading, and a convincing visible connection to the source scene. Review color-assisted selections and traced contours for holes, clipped seams, detached folds, fringes, and artificial straight edges. A contour can be geometrically valid while the overlap still looks pasted on. Redesign the foreground or backing where needed, and inspect the final composed join at enlarged, standard, and thumbnail sizes. +Prototype the Normal composition with the approved illustration and real text before creating other printing faces or material maps. Preserve each rejected candidate as a review revision when comparison remains useful. After explicit Normal-layout approval, derive the other printings and all affected masks once from the selected sources and geometry. -Validate actual unclipped glyph bounds and require no foreground/lettering intersection. For a backed layout, verify glyphs remain on the intended legible backing field after its shapes are adjusted. Record visual assessment separately from numerical connectivity or source-preservation checks. During prototyping, make only specifically requested provisional previews; final printings and masks follow approval of the selected illustration and adapted foreground. +Choose backing placement from the scene's negative space and measured text needs. Legendary backings may be asymmetric or subject-specific. Size them to the approved text, reference, and breathing room; when an empty region appears accidental, test a smaller or differently placed backing before adding decorative content. Keep the card frame/backing visually primary and limit illustrated foreground restoration to the intended exception. + +Choose a foreground silhouette that preserves the complete logical form, internal leading, and a convincing visible connection to the source scene. Follow material and garment seams instead of selecting by color alone. Include connected lining, cuffs, and folds when they form one object; exclude detached shards, lower robe panels, background posts, or other unrelated components. Review color-assisted selections and traced contours for holes, clipped seams, fringes, and artificial straight edges. A contour can be numerically valid while the overlap still looks pasted on. + +Place all fitted components on the same 2000 × 2800 canvas with explicit transforms and a common alpha convention. Render masks and edges at sufficient resolution, composite color with premultiplied alpha, and downsample without transparent-RGB halos. Inspect the complete form and its join at enlarged, standard, and thumbnail sizes. + +Validate actual unclipped glyph bounds and require no foreground/lettering intersection. For a backed layout, verify every glyph remains on the intended opaque field after its shape changes. For irregular or asymmetric layouts, define the usable text band after foreground obstructions. Record foreground component bounds, text clearance, source hashes, and visual assessment separately from numerical connectivity checks. Select components explicitly per printing. A backing-specific adaptation appears in Normal and Borderless. Textless uses the full illustration plus its selected outer frame; Boundless uses the full illustration alone. Omit the backing-specific foreground from those two printings to avoid duplicating or changing the underlying subject unexpectedly. Record a revised full-bleed artwork variant if an adaptation is intended for all printings. @@ -104,6 +110,8 @@ For a centered title, the selected template supplies the horizontal center ancho Every template needs approved minimum font sizes and maximum line counts, established at actual application display sizes. Word count is an editorial guide, not a fit check. Long names and wide glyphs can overflow even with few words. +Choose verse line breaks by semantic phrasing before reducing the font. Keep dependent phrases together where the available region permits; avoid line endings that create false pauses after incomplete constructions. Then fit the largest approved size with natural tracking. For an asymmetric Legendary layout, measure the actual foreground obstruction and center the union of verse ink within the remaining usable band above the fixed reference region. + Validate the **unclipped rendered glyph bounds** against each text region and its padding. Checking only the text object's box can miss clipping. Reject missing fonts, missing glyphs, overflow, or insufficient line capacity; do not silently substitute fonts, crop the text, or shrink it below the minimum. Use a reviewed alternate layout or revise the content. Text is rendered anew when card content changes, but its coordinates do not change randomly. Once approved, the frame/backing decoration is reused. @@ -242,11 +250,12 @@ No GIMP MCP is connected in this session. Select and verify a specific adapter's 1. Render the template with plain fills and a typography stress set. Establish acceptable proportions, font sizes, and overlap. 2. Prototype the illustration using the compact art brief and selected layout guide. Fit/review it and obtain explicit artwork approval before proceeding to assembly. -3. Add restrained Common frame decoration only if plain vector treatment is insufficient. -4. Assemble all four printings with the shared `raw-ridges-v1` recipe for new stained-glass art, resolving each printing’s actual visible components. Keep any explicitly requested alternative as a separate comparison. -5. Run structural checks and review seams and text at target sizes. -6. Review with the approved harness finish response and compare against the existing reference fixture. -7. Freeze the approved template/assets and reuse them for subsequent Commons. +3. For a bespoke Legendary, record the depth plan and build a Normal-only composition with real text. Review backing placement, complete foreground form, semantic line breaks, joins, and card-size hierarchy. Obtain explicit Normal-layout approval before generating the remaining printings or masks. +4. Add restrained tier decoration only when plain vector treatment is insufficient; first reduce or reshape accidental empty backing areas. +5. Assemble all four printings with the shared `raw-ridges-v1` recipe for new stained-glass art, resolving each printing’s actual visible components. Keep any explicitly requested alternative as a separate comparison. +6. Run structural checks and review seams and text at target sizes. +7. Review with the approved harness finish response and compare against the existing reference fixture. +8. Freeze the approved template/assets. Reuse tier templates where required; preserve bespoke Legendary templates with their selected card revision. Keep the current harness reference fixtures available for comparison. This proof should establish new production assets without silently replacing the approved material baseline. diff --git a/docs/sanctification-master-card-catalog-fixed-v3.xlsx b/docs/sanctification-master-card-catalog-fixed-v3.xlsx deleted file mode 100644 index caf4f19..0000000 Binary files a/docs/sanctification-master-card-catalog-fixed-v3.xlsx and /dev/null differ diff --git a/docs/sanctification-master-card-catalog-v20.xlsx b/docs/sanctification-master-card-catalog-v20.xlsx deleted file mode 100644 index 6dc5bad..0000000 Binary files a/docs/sanctification-master-card-catalog-v20.xlsx and /dev/null differ diff --git a/docs/sanctification-master-card-catalog.xlsx b/docs/sanctification-master-card-catalog.xlsx new file mode 100644 index 0000000..f72a87f Binary files /dev/null and b/docs/sanctification-master-card-catalog.xlsx differ diff --git a/in-progress/cards/BP-001-moses/card.json b/in-progress/cards/BP-001-moses/card.json new file mode 100644 index 0000000..7ad5d28 --- /dev/null +++ b/in-progress/cards/BP-001-moses/card.json @@ -0,0 +1,35 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "title": "Moses", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "history": [ + { + "revision": "v01", + "path": "history/revisions/v01", + "status": "superseded-revision-record" + }, + { + "revision": "v02", + "path": "history/revisions/v02", + "status": "superseded-revision-record" + }, + { + "revision": "v03", + "path": "history/revisions/v03", + "status": "superseded-revision-record" + }, + { + "revision": "v04", + "path": "history/revisions/v04", + "status": "superseded-revision-record" + } + ], + "selectedRevision": "v05", + "stage": "approved", + "approval": { + "by": "user", + "note": "This looks great. This iteration is approved" + } +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/README.md b/in-progress/cards/BP-001-moses/history/revisions/v01/README.md new file mode 100644 index 0000000..93c294d --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/README.md @@ -0,0 +1,17 @@ +# BP-001 — Moses — v01 + +Provisional Legendary illustration, direction **The Threshold**. Art direction and prompt by Astra medium; raster generated with the built-in image tool. Awaiting user review. + +- `source/art-native.png`: unchanged generated native output. +- `source/prompt.txt`: exact generation prompt. +- `source/concepts.md`: alternatives, selected concept and bespoke blend direction. +- `source/generation.json`: tool/source provenance. +- `source/art-fit.json`: native size, uniform fit, crop and hashes. +- `source/art-master.png`: fitted 2000×2800 artwork. +- `review/art-review.png`: 1000×1400 art review. +- `review/art-thumbnail.png`: 250×350 focal readability review. +- `review/illustration-review.md`: strengths, risks and prospective overlap. + +Reproduce fitted art and review sizes with `python3 source/fit-art.py`. This does not regenerate artwork or assemble card products. Native generation is stochastic; the exact original source is retained. + +The complete hanging tent curtain is the proposed concrete foreground connection for later bespoke backing integration. No production backings, lettering, masks or printings exist yet. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/card.json b/in-progress/cards/BP-001-moses/history/revisions/v01/card.json new file mode 100644 index 0000000..5961927 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/card.json @@ -0,0 +1,22 @@ +{ + "cardId": "BP-001", + "title": "Moses", + "name": "MOSES", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "artApproval": null, + "reference": "Deuteronomy 34:10", + "translation": "ESV", + "referenceDisplay": "Deuteronomy 34:10 \u2022 ESV", + "excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face", + "direction": "The Threshold", + "artStatus": "Provisional illustration awaiting user review", + "layoutStatus": "Bespoke Legendary layout pending; no production assembly authorized", + "sceneConstraints": [ + "Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.", + "Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.", + "No Red Sea or Sinai action; these belong to event cards." + ], + "art": "in-progress/cards/BP-001-moses/revisions/v01/source/art-master.png", + "artSHA256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/full-manifest.json b/in-progress/cards/BP-001-moses/history/revisions/v01/full-manifest.json new file mode 100644 index 0000000..00ac357 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/full-manifest.json @@ -0,0 +1,48 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v01", + "stage": "art-review", + "approval": null, + "content": "card.json", + "files": { + "README.md": { + "sha256": "33e07c5325e22b883784a205cfd013d18db57474983e7aabd3b0ff39bd1e21f3" + }, + "card.json": { + "sha256": "c84644aeea69fb47f4bd561035d9afc7f42f163e4aa47d11b876a1ef93cdb1fb" + }, + "source/art-fit.json": { + "sha256": "e35c43cd69fdcb82f9eaade078829d8a6fef867744b499ddda16ae1897ac41f6" + }, + "source/art-master.png": { + "sha256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3" + }, + "source/art-native.png": { + "sha256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615" + }, + "source/concepts.md": { + "sha256": "ad904bab23d6cf4a1362007456757c4ee6748f19c00adf4ee83db507a59e9412" + }, + "source/fit-art.py": { + "sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07" + }, + "source/generation.json": { + "sha256": "aed55cc70ecb79e9d92e58eabfd99af5cb54c60afd92527d0183fccd4383bbdd" + }, + "source/prompt.txt": { + "sha256": "3029fdda719a4011d29b424be10e1f01e0a1893f912c7a35401bcf9e3f43a148" + }, + "low/manifest.json": { + "sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b" + }, + "med/manifest.json": { + "sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6" + }, + "high/manifest.json": { + "sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a" + } + }, + "compatibilityAliases": [] +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/record.json b/in-progress/cards/BP-001-moses/history/revisions/v01/record.json new file mode 100644 index 0000000..10a2480 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/record.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": 1, + "kind": "compact-card-revision", + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v01", + "originalStage": "art-review", + "approval": null, + "reason": "superseded when v05 was accepted", + "originalManifest": "full-manifest.json", + "originalManifestSHA256": "a518aa6886d250d20fdee62315ca54fd14d2ef58026962dacb654cf477f09ef9", + "referenceResolution": "low", + "referenceImages": [ + "review/art-reference.png" + ], + "retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/review/art-reference.png b/in-progress/cards/BP-001-moses/history/revisions/v01/review/art-reference.png new file mode 100644 index 0000000..929f7db Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v01/review/art-reference.png differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/review/illustration-review.md b/in-progress/cards/BP-001-moses/history/revisions/v01/review/illustration-review.md new file mode 100644 index 0000000..a651b4b --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/review/illustration-review.md @@ -0,0 +1,26 @@ +# Moses v01 — provisional illustration review + +Status: **art-review; no user artwork approval and no assembly**. Astra medium authored the concept and prompt. The built-in image tool rendered one provisional study. Inspected its returned native image and the saved thumbnail (250×350). The image-view tool failed sandbox initialization; thumbnail inspection used a JPEG transported from the saved review image. No GPU or moving-light review applies to this art-only study. + +## What works + +- The governing threshold idea reads immediately: Moses turns to the presence beside the tent, with the people’s camp behind him. Folded hands and a lowered gaze express responsibility and attention rather than triumph. +- Pose is coherent. Both hands belong to visible forearms; resting fingers and wrist overlap read plausibly. The robe has weight, shoulders connect clearly and no theatrical action competes with the face. +- Tent curtain, flowing robes, clouds, sky, mountains and broad ground forms are built from meaningful glass panes. Clothing divisions follow fold direction. Dark leading and the sapphire/burgundy/ivory color masses organize the thumbnail strongly. +- Cloud is luminous but non-anthropomorphic. No literal divine figure, horns, tablets, Sinai or Red Sea motif appears. Cloud light connects the face, inner garment and tent threshold. +- Tent opening is readable at normal and thumbnail scale. Rows of tents recede into the right background and establish the people Moses serves. +- Face and folded hands are well above a prospective lower-quarter verse region. The top-right sky offers title clearance under the David surrogate. Full-bleed lower scene remains complete. + +## Risks and next refinement + +- Face, hands and beard lean toward conventional detailed illustration: realistic wrinkles, veins and hair strands exceed the desired stylized pane modeling. Before art lock, consider a targeted edit simplifying skin into expressive bounded areas and quieting microtexture without altering expression, hand ownership or pose. +- Camp contains small figures, animal and vegetation details beyond what the scene needs. It remains subordinate but could simplify into fewer grouped panes. +- Cloud has scalloped bulb-like repetition; it works as assembled glass but could gain broader, less repetitive flowing shapes if revising. It is brighter than the face; assess whether its brightness competes with Moses on the composed card. +- Environmental surface is pronounced and also carries through the robe. Its grain remains below the leading at thumbnail scale; keep any future skin correction much gentler than the environment. +- This is a waist-to-feet cropped full-height presence, not a literal full-body image with visible feet. Lower robe continues through the bottom edge intentionally. + +## Bespoke blend feasibility + +The left burgundy curtain is visibly attached to the tent lintel and hangs to a complete hem. Its lower folds can later cross a backing’s left edge while remaining visibly attached above; this is a stronger structural overlap candidate than a detached cloud fragment. The adjacent luminous cloud can inform a curved panel boundary, but avoid wrapping decorative cloud around lettering. The exact panel shape is unresolved. Do not simply reuse David’s mantle geometry or cut off the curtain where a backing hides its base. + +No frame, backing, text, masks, printings, finish coverage or harness fixtures were generated. Production assembly remains deferred until the user concludes the artwork loop. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/source/art-fit.json b/in-progress/cards/BP-001-moses/history/revisions/v01/source/art-fit.json new file mode 100644 index 0000000..4329421 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/source/art-fit.json @@ -0,0 +1,24 @@ +{ + "source": "art-native.png", + "sourceDimensions": [ + 1060, + 1484 + ], + "canvas": [ + 2000, + 2800 + ], + "method": "uniform centered cover fit", + "scale": 1.8867924528301887, + "scaledDimensions": [ + 2000.0, + 2800.0 + ], + "cropOffset": [ + 0.0, + 0.0 + ], + "resampling": "Pillow LANCZOS", + "sourceSHA256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615", + "outputSHA256": "5bda2e8e5610f4e47eb875d09c024208953c91eb204c0cb5871e853fc81f11b3" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/source/concepts.md b/in-progress/cards/BP-001-moses/history/revisions/v01/source/concepts.md new file mode 100644 index 0000000..4040e25 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/source/concepts.md @@ -0,0 +1,19 @@ +# Moses — composition exploration + +BP-001 / Moses / Legendary, catalogue identity confirmed from `docs/sanctification-master-card-catalog.xlsx`. The catalogue reserves Red Sea and Sinai actions for their event cards and describes Moses as “Authority with dependence.” The user supplied the exact verse excerpt; the reference spelling is normalized to Deuteronomy. + +1. **The Threshold — selected.** Moses stands beside the tent opening, turned quietly toward the presence while the camp recedes behind. The lighting connects face, curtain and threshold. This most directly holds intimacy and responsibility in one still image. +2. **Returning to the people.** Moses steps from the tent toward the camp, looking back. Strong narrative movement, but departure becomes the action and weakens the requested calm. +3. **Held in the cloud.** A close portrait against an enclosing cloud and a very small camp. Iconic scale, but risks theatrical radiance and loses the tent/camp relationship. + +## Bespoke Legendary direction + +Explore the tent threshold as the later boundary between illustration and backing. A complete curtain attached to the tent can carry its hanging folds over a future backing edge; broad cloud panes can curve around that edge. This would connect sacred presence to the card’s physical composition without repeating David’s mantle. The curtain is the primary concrete overlap candidate; cloud is an atmospheric alternative, not a mandated special effect. Do not extract or assemble either until artwork approval and a composed-preview request. + +## Provisional layout assumptions + +No Moses tier template exists. David’s accepted bespoke layout is a clearance surrogate only: its title occupies the upper-right approximately x1048–1942/y58–365 on the 2000×2800 master, and verse backing begins around y2075 in x466–1942. Moses’s later layout must be bespoke, not a David clone. Generation reserves quiet upper sky and lower-right scene continuation; focal face, hands and threshold should remain above the lower quarter. Covered regions still receive complete full-bleed art. No guide overlay or final panel geometry is generated at this stage. + +## Model and provenance + +Art direction and prompt authored by Astra with medium reasoning, as requested. Raster generation uses the built-in image generation tool; the direction model is distinct from the image renderer. Exact generation prompt is retained in `prompt.txt`. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/source/fit-art.py b/in-progress/cards/BP-001-moses/history/revisions/v01/source/fit-art.py new file mode 100644 index 0000000..87eaac2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/source/fit-art.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Reproduce only the fitted art and review sizes; never assemble a card.""" +from pathlib import Path +from PIL import Image, ImageOps +import hashlib,json +root=Path(__file__).resolve().parent.parent +native=root/'source/art-native.png' +with Image.open(native) as source: + im=source.convert('RGB');w,h=im.size + scale=max(2000/w,2800/h) + art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5)) + art.save(root/'source/art-master.png') + for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]: + art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name) +fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()} +(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/source/generation.json b/in-progress/cards/BP-001-moses/history/revisions/v01/source/generation.json new file mode 100644 index 0000000..c611e81 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/source/generation.json @@ -0,0 +1,13 @@ +{ + "mode": "built-in imagegen", + "artDirectionModel": "gpt-6-astra", + "artDirectionReasoning": "medium", + "prompt": "prompt.txt", + "native": "art-native.png", + "originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-d3da0af1-8b0e-49d3-a877-da339325b2c3.png", + "referenceImages": [], + "fit": "art-fit.json", + "reproduceFit": "python3 source/fit-art.py", + "generationReproducibility": "Exact prompt and native bytes retained; image generation is stochastic, fit and review resizing are deterministic.", + "approval": null +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v01/source/prompt.txt b/in-progress/cards/BP-001-moses/history/revisions/v01/source/prompt.txt new file mode 100644 index 0000000..faf95f5 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v01/source/prompt.txt @@ -0,0 +1,9 @@ +Use case: historical-scene +Asset type: provisional base illustration for Sanctification Legendary card BP-001 Moses; full-bleed portrait 5:7 art only. +Primary request: Moses stands just outside the tent of meeting after speaking with the Lord. The divine cloud remains near the tent entrance; the Israelite camp recedes into the background. Reverent intimacy with God, solemn responsibility, quiet authority, sacred awe. His presence is weight-bearing and calm, not triumphant or theatrical. + +Composition: One governing idea, the threshold between holy presence and responsibility for the people. Moses is a substantial upright three-quarter figure slightly right of center, head near 48% x / 28% y, shoulders below the upper fifth; face and hands fully readable in the middle field. His head turns gently toward the tent at left, eyes lowered in reflective attention, shoulders settled with the gravity of service. An elderly Near Eastern man with expressive lined face and a long pale beard, dressed in substantial deep sapphire and muted wine-colored folded robes, warm sand-colored inner garment. One hand rests quietly over the other near his waist, hands anatomically legible with clear forearm connections. No triumphant raised arms. The tent is directly behind and left of him, recognizable woven ochre walls and a deep dark opening, with one complete hanging entrance curtain attached at the upper-left and falling in broad weighty folds toward the lower-left. A restrained luminous ivory and pale gold cloud curves downward at the entrance beside him, built of broad interlocking glass forms. Its light touches the turned face and the nearer garment edge; its source is the holy cloud, not a literal divine figure. Distant rows of modest Israelite tents recede far behind to the right across quiet desert terrain, smaller and lower-contrast than Moses. Leave the topmost 10% as quiet sky and the lower-right quarter as naturally subdued ground/robe continuation that could later sit beneath a text backing; do not draw panels or blank spaces. Keep his face, both hands, and the tent threshold above the lowest quarter. The complete lower-left tent curtain and adjacent cloud create a natural connected silhouette for a future bespoke foreground overlap; this image itself is continuous, complete artwork. + +Style/medium: Deliberate stained-glass construction, colored glass panes joined by coherent dark lead-like divisions. The panes build silhouettes and form from the outset, never a crack pattern pasted over a painting. Robes use long flowing curved panes following folds; beard and hair use purposeful expressive areas; face and hands retain fine expressive details inside a few carefully shaped glass areas, without heavy leading crossing every feature. Clouds are broad curved luminous panes, tent fabric folds long cohesive panes, terrain angular and flowing interlocking segments. No photographic skin or sky. Tactile lightly etched or brushed satin glass, diffuse pane-contained cloudy variation strongest in environmental panes, very gentle on face and hands. Varied pane size with quiet large areas and selective detail. Luminous deep sapphire, wine, muted turquoise, amber and ivory; tasteful gold accents and a cohesive warm cloud / cool camp lighting relationship. Legendary refinement comes from iconic composition, beautiful selective light, depth and restraint; no excess cracks or ornament. Soft bounded hue shifts and sparse shaped highlights inside selected panes, many panes quiet. Leading and major color shapes must organize the scene at thumbnail size. + +Constraints: Full-bleed 5:7 artwork only, complete intentional scene even in areas a later backing could cover. No lettering, no scripture text, no card frame, no border, no backing panels, no corner ornaments, no watermark. No tablets, no Red Sea, no Sinai spectacle, no horns, no literal face/body of God, no halo disk. No triumphal hero pose, fantasy armor, lightning, neon glow, particles, glitter, dotted glints, uniform mosaic grid, random cracking or photorealism. \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/README.md b/in-progress/cards/BP-001-moses/history/revisions/v02/README.md new file mode 100644 index 0000000..8ef9973 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/README.md @@ -0,0 +1,16 @@ +# BP-001 — Moses — v02 + +**The Threshold — glass-language refinement.** Astra medium direction; built-in image edit. Composition retained from v01, with Moses and background translated into clearer stylized glass planes. Pending user artwork review. + +- `review/art-review.png`: 1000×1400 review. +- `review/art-thumbnail.png`: 250×350 review. +- `review/illustration-review.md`: assessment and tradeoffs. +- `source/art-native.png`: unchanged generated output. +- `source/art-master.png`: fitted 2000×2800 illustration. +- `source/edit-prompt.txt`: exact edit instructions. +- `source/edit-reference.jpg`: actual conversation-visible edit input. +- `source/generation.json`: source/edit provenance and hashes. +- `source/art-fit.json`: uniform fit record. +- `source/refinement.md`: scope and input fallback explanation. + +Run `python3 source/fit-art.py` from this revision to reproduce the fit and review sizes. Image editing itself is stochastic. No production layers or printings are assembled; v01 remains preserved. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/card.json b/in-progress/cards/BP-001-moses/history/revisions/v02/card.json new file mode 100644 index 0000000..90d4507 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/card.json @@ -0,0 +1,27 @@ +{ + "cardId": "BP-001", + "title": "Moses", + "name": "MOSES", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "artApproval": null, + "reference": "Deuteronomy 34:10", + "translation": "ESV", + "referenceDisplay": "Deuteronomy 34:10 \u2022 ESV", + "excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face", + "direction": "The Threshold", + "artStatus": "Provisional v02 glass-language refinement awaiting user review", + "layoutStatus": "Bespoke Legendary layout pending; no production assembly authorized", + "sceneConstraints": [ + "Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.", + "Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.", + "No Red Sea or Sinai action; these belong to event cards." + ], + "compositionApproval": { + "by": "user", + "scope": "v01 composition only; visual rendering requested to change" + }, + "revisionIntent": "Preserve composition; reduce figure and background realism into meaningful glass planes.", + "art": "in-progress/cards/BP-001-moses/revisions/v02/source/art-master.png", + "artSHA256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/full-manifest.json b/in-progress/cards/BP-001-moses/history/revisions/v02/full-manifest.json new file mode 100644 index 0000000..409069e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/full-manifest.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v02", + "stage": "art-review", + "approval": null, + "content": "card.json", + "files": { + "README.md": { + "sha256": "4c1e8c66a5f78e4149e9306ccb14416f2b371d2b6d054984a750a330b966046b" + }, + "card.json": { + "sha256": "872fc613c73be7628042efcc31cb005442e5d2b9c0513f34795df5f3db868e36" + }, + "source/art-fit.json": { + "sha256": "1f32b29c1d506ab758358c8a109215152ee8aa41d01f02f43429da4afba734d9" + }, + "source/art-master.png": { + "sha256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88" + }, + "source/art-native.png": { + "sha256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d" + }, + "source/edit-prompt.txt": { + "sha256": "51ddbfbd53f9e85db0aa33d68e3b65c21a420390bdbab2fbe244aeebd53cb77b" + }, + "source/edit-reference.jpg": { + "sha256": "a97ed77927b5aab8953f9931e51697964d6ee5b55db13a265cfdda6dcc07eaba" + }, + "source/fit-art.py": { + "sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07" + }, + "source/generation.json": { + "sha256": "312243f950f859e0c78584628c09d043f9c3a1863be8fcf59f36a16ea1de4f34" + }, + "source/refinement.md": { + "sha256": "e5242baa1dc8837034a17e75c992fe648f95571df4869b13c3931732889c6636" + }, + "low/manifest.json": { + "sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b" + }, + "med/manifest.json": { + "sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6" + }, + "high/manifest.json": { + "sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a" + } + }, + "compatibilityAliases": [] +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/record.json b/in-progress/cards/BP-001-moses/history/revisions/v02/record.json new file mode 100644 index 0000000..a71936d --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/record.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": 1, + "kind": "compact-card-revision", + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v02", + "originalStage": "art-review", + "approval": null, + "reason": "superseded when v05 was accepted", + "originalManifest": "full-manifest.json", + "originalManifestSHA256": "61a59ae93df4e9a3c1feb09922301f4f718b9302c744a062e53e016b26edd68d", + "referenceResolution": "low", + "referenceImages": [ + "review/art-reference.png" + ], + "retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/review/art-reference.png b/in-progress/cards/BP-001-moses/history/revisions/v02/review/art-reference.png new file mode 100644 index 0000000..cd36d5a Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v02/review/art-reference.png differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/review/illustration-review.md b/in-progress/cards/BP-001-moses/history/revisions/v02/review/illustration-review.md new file mode 100644 index 0000000..ddfecc2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/review/illustration-review.md @@ -0,0 +1,24 @@ +# Moses v02 — targeted glass-language refinement + +**Art-review; user approval pending.** The user selected v01’s composition and requested less realism in Moses and the background. This v02 edits that artwork with Astra medium direction and the built-in image tool. Inspected the returned full native image and saved 250×350 thumbnail. Compared visually with the approved Calling of the First Disciples glass reference. + +## Result + +Realism is substantially reduced. Face, hair, beard, hands, camp, mountains and ground now read as designed glass forms rather than realistic illustration with incidental divisions. This is sufficient to present as a direct answer to the requested style correction, not a claim that the user has accepted it. + +- Face is organized into amber, gold and umber planes. Brow, eyelid, nose and mouth preserve the quiet left-facing expression; realistic wrinkles and skin texture have largely gone. +- Hair and beard use broad ivory and cool-gray flowing locks with dark boundaries. Individual realistic hairs are gone. Their curved segmentation remains expressive. +- Folded hands preserve visible forearm ownership and finger contours. Veins and wrinkles have been removed; broad warm planes model their overlap. Some lighter internal hand shapes remain painterly, but no longer read as realistic skin. No obvious extra hand or disconnected wrist is visible. +- Robes and curtain retain the original color arrangement and flow. Their panes have stronger leading and fewer realistic cloth shadows. The curtain remains visibly attached at the lintel and has its complete lower hem. +- Camp is simplified into grouped tents and small graphic silhouettes. People and an animal remain as subordinate shapes rather than detailed miniatures. Mountains and ground now consist of explicit bounded planes. The more graphic foliage forms are repetitive in places but remain secondary. +- Cloud remains luminous, non-anthropomorphic and located at the tent entrance. Its curved panes connect the light to Moses. No new symbols or narrative events appeared. +- Pose, framing, tent relationship, camp recession, title clearance and lower-quarter clearance remain substantially intact. The future connected curtain overlap is still viable. +- At thumbnail scale, sapphire figure, burgundy curtain and bright cloud stay distinct; pane boundaries now visibly organize the full scene. The focal face remains readable. + +## Tradeoffs / remaining review points + +Leading is heavier and more graphic than v01 and somewhat stronger than the Disciples reference. This makes the style correction decisive, but the user may prefer a slightly gentler balance. Face geometry and beard locks are intentionally simplified, while a few shaded transitions remain inside panes. Surface texture is still visible throughout robes and environment; it does not replace the panes. The cloud is still a bright repeated scalloped form, retained to preserve the accepted composition. No further edit was made without review. + +Direct native-path input failed because the filesystem sandbox helper could not initialize. The successful edit used the already-visible 700×980 JPEG reference; the exact reference is retained in `source/edit-reference.jpg`, with provenance and hashes in `source/generation.json`. Native output is saved unchanged. Fitting to 2000×2800 and review resizing are recorded and reproducible. + +No frame, backing, text, mask, printing, finish or harness output was built. v01 remains preserved. No GPU or moving-light review applies to this art-only pass. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/source/art-fit.json b/in-progress/cards/BP-001-moses/history/revisions/v02/source/art-fit.json new file mode 100644 index 0000000..762ef8c --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/source/art-fit.json @@ -0,0 +1,24 @@ +{ + "source": "art-native.png", + "sourceDimensions": [ + 1060, + 1484 + ], + "canvas": [ + 2000, + 2800 + ], + "method": "uniform centered cover fit", + "scale": 1.8867924528301887, + "scaledDimensions": [ + 2000.0, + 2800.0 + ], + "cropOffset": [ + 0.0, + 0.0 + ], + "resampling": "Pillow LANCZOS", + "sourceSHA256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d", + "outputSHA256": "ce37c2e61dc79b089ef69ed673253cd63912b9ef0d012798f8f0f2a9c9d85b88" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/source/edit-prompt.txt b/in-progress/cards/BP-001-moses/history/revisions/v02/source/edit-prompt.txt new file mode 100644 index 0000000..8a9b58c --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/source/edit-prompt.txt @@ -0,0 +1,19 @@ +Use case: style-transfer +Asset type: Sanctification Legendary Moses BP-001 v02, targeted edit of the supplied v01 artwork. +Input image: the existing Moses illustration is the edit target. Preserve its composition and identity exactly; change its visual construction throughout. + +Primary request: Translate this entire illustration much more decisively into designed stained and painted glass. The present Moses and camp are too realistic. This is not a new scene and not a crack overlay. Rebuild the visual rendering inside the existing silhouettes as meaningful colored glass pieces joined by dark leading, using stylized expressive forms, broad bounded areas of color, and selectively intricate shards. Make the difference unmistakable at normal viewing size. + +Keep unchanged: portrait 5:7 canvas; Moses's size and position, elderly Near Eastern identity, left-facing reflective expression, lowered eyes, quiet authority and burdened calm; exact head turn and body stance; both folded hands and their clear forearm ownership; sapphire, burgundy and ivory robe arrangement; tent at left and its dark doorway; complete attached burgundy curtain with its hanging folds and visible hem; luminous non-anthropomorphic ivory/gold cloud; distant Israelite camp to the right; mountain horizon; warm presence / cool camp lighting relationship. Preserve the curtain’s continuity for a future foreground overlap. No new action, props or symbols. + +Required changes: +- Moses’s face: designed warm amber, cream and umber facial planes with clear brow, eyelid, nose and mouth. Simplify wrinkles and all flesh shading into a small number of expressive bounded shapes. Stylized flat glass anatomy, not realistic skin with lead lines. Gentle transitions only inside a few panes. Retain age through dignified contour and expression, not skin pores or creases. +- Hands: preserve the existing folded gesture and plausible fingers but reduce veins, tendons, wrinkles and realistic modeling. Distinct legible finger silhouettes with a few warm bounded planes and restrained painted-glass detail, no arbitrary black grid across fingers. +- Hair and beard: replace realistic individual strands with flowing ivory, pale gold and cool gray locks made of expressive connected glass segments. Broad contour rhythms and selected fine painted marks only, no photorealistic wisps. +- Robes and curtain: keep their existing flowing structure, enrich chosen folds with meaningful smaller jewel panes, but reduce continuous realistic fabric modeling and grain. Broad quiet glass areas should alternate with detail. +- Camp and land: decisively simplify tiny people, animals, foliage, tent fabric detail and realistic rocks into grouped colored tent silhouettes and interlocking ground panes. Tents remain clearly tents, decreasing in size into the distance, but not a realistic miniature landscape. Mountains become broad purposeful angular blue/violet panes with crisp boundary changes and little atmospheric softness. +- Sky and cloud: coherent broad curved glass shapes, selected intricate shards near luminous cloud edges and sky accents, substantial quiet panes elsewhere. Avoid uniform tessellation. Keep the luminous cloud beside the tent without introducing God as a figure. +- Surface: tactile lightly brushed or etched glass with restrained cloudy satin variation contained within panes, not a uniform noise overlay. Very gentle texture on face/hands. Sparse purposeful shaped highlights, never pervasive sparkles. + +Overall: unmistakable stained-glass image construction throughout the focal figure AND environment, with dark leading describing real form and material changes. Expressive sacred illustration, stylized and iconic rather than realistic painting. A few intricate regions are welcome; not every surface should have the same shard size or density. Preserve depth through overlap, scale and color hierarchy rather than realistic gradients. +No text, letters, card frame, backing, border, watermark, halo disk, horns, tablets, lightning, particles, photographic skin, realistic landscape rendering, crack-pattern overlay, uniform polygon grid or all-over micro-mosaic. \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/source/fit-art.py b/in-progress/cards/BP-001-moses/history/revisions/v02/source/fit-art.py new file mode 100644 index 0000000..87eaac2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/source/fit-art.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Reproduce only the fitted art and review sizes; never assemble a card.""" +from pathlib import Path +from PIL import Image, ImageOps +import hashlib,json +root=Path(__file__).resolve().parent.parent +native=root/'source/art-native.png' +with Image.open(native) as source: + im=source.convert('RGB');w,h=im.size + scale=max(2000/w,2800/h) + art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5)) + art.save(root/'source/art-master.png') + for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]: + art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name) +fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()} +(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/source/generation.json b/in-progress/cards/BP-001-moses/history/revisions/v02/source/generation.json new file mode 100644 index 0000000..a22ac4e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/source/generation.json @@ -0,0 +1,16 @@ +{ + "mode": "built-in imagegen edit", + "artDirectionModel": "gpt-6-astra", + "artDirectionReasoning": "medium", + "prompt": "edit-prompt.txt", + "native": "art-native.png", + "originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-1298335b-5086-41b2-ad33-6d19aad9c690.png", + "reference": "edit-reference.jpg", + "referenceSHA256": "a97ed77927b5aab8953f9931e51697964d6ee5b55db13a265cfdda6dcc07eaba", + "referenceProvenance": "700x980 JPEG quality 82 made from v01 source/art-native.png and shown in conversation; direct native-path edit failed sandbox initialization, so num_last_images_to_include=1 was used.", + "priorNative": "../../v01/source/art-native.png", + "priorNativeSHA256": "722b61d2beef84751675b98a32fa1b218583260107e4712432979c0c67a11615", + "fit": "art-fit.json", + "generationReproducibility": "Stochastic targeted edit; exact prompt, actual displayed reference and native output retained; fit and review sizes deterministic.", + "approval": null +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v02/source/refinement.md b/in-progress/cards/BP-001-moses/history/revisions/v02/source/refinement.md new file mode 100644 index 0000000..b8c342e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v02/source/refinement.md @@ -0,0 +1,5 @@ +# Moses v02 — glass language refinement + +The user selected v01 composition but requested less realism in Moses and the background. Preserve the silhouette, pose, expression, hand ownership, tent/cloud/camp staging and future connected curtain overlap. Translate the figure and environment into deliberate glass construction with broader expressive planes, selected intricate shards, and restrained within-pane modeling. No production assembly is authorized. + +Astra medium authored the targeted edit prompt. Direct file-path edit input failed because the filesystem sandbox helper could not initialize. The built-in tool therefore edits the most recent visible conversation image: a 700×980 JPEG preview of v01’s exact native output, saved as `edit-reference.jpg`. This is the inspected reference, not a new composition. The full-resolution v01 native remains preserved in its prior revision. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/README.md new file mode 100644 index 0000000..0c580fd --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/README.md @@ -0,0 +1,20 @@ +# BP-001 — Moses — v03 + +**The Threshold — Legendary intricacy.** Astra medium direction; built-in targeted image edit. Retains v02’s stylized figure and composition while adding curated glass detail to cloud, curtain, robes, tent and selected environment forms. Artwork selected for bespoke layout review. + +- `review/art-review.png`: 1000×1400 review. +- `review/art-thumbnail.png`: 250×350 review. +- `review/illustration-review.md`: assessment and tradeoffs. +- `source/art-native.png`: unchanged generated output. +- `source/art-master.png`: fitted 2000×2800 illustration. +- `source/edit-prompt.txt`: exact edit prompt. +- `source/edit-reference.jpg`: actual displayed v02 edit input. +- `source/generation.json`: source/edit provenance and hashes. +- `source/art-fit.json`: uniform fit record. +- `source/refinement.md`: scope and input fallback. + +Run python3 source/fit-art.py from this revision to reproduce the fitted art and review sizes. Image generation is stochastic. Earlier revisions remain preserved. + +## Provisional Legendary composition + +[Threshold composition preview](review/layout-preview-v1/README.md) tests a bespoke gold/lapis frame, upper-right title plaque, lower textile backing, real P052 lettering, and an authored attached-curtain foreground. This is an explicitly requested layout preview only. No production printings, finish masks, text masks, or harness fixture were generated. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/card.json b/in-progress/cards/BP-001-moses/history/revisions/v03/card.json new file mode 100644 index 0000000..79585d8 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/card.json @@ -0,0 +1,48 @@ +{ + "cardId": "BP-001", + "title": "Moses", + "name": "MOSES", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "artApproval": { + "by": "user", + "note": "I like the art a lot.", + "scope": "BP-001 Moses v03 illustration; layout preview authorized separately." + }, + "reference": "Deuteronomy 34:10", + "translation": "ESV", + "referenceDisplay": "Deuteronomy 34:10 \u2022 ESV", + "excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face", + "direction": "The Threshold", + "artStatus": "v03 illustration selected for bespoke layout review", + "layoutStatus": "Provisional Legendary compact textile composition under review/layout-preview-v2; production assembly not authorized.", + "sceneConstraints": [ + "Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.", + "Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.", + "No Red Sea or Sinai action; these belong to event cards." + ], + "compositionApproval": { + "by": "user", + "scope": "v01 composition only; visual rendering requested to change" + }, + "revisionIntent": "Preserve v02 glass stylization and composition; add curated hierarchical glass intricacy for Legendary presence.", + "art": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "artSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "layoutCandidate": { + "id": "BP-001-moses-threshold-v2", + "preview": "review/layout-preview-v2/composed-preview-1000.png", + "governingIdea": "Tent threshold with compact woven screen", + "foreground": "Attached burgundy curtain crossing the frame and meeting the stepped left edge of the verse backing", + "typography": { + "titleSize": 138, + "verseSize": 72, + "verseBaselineGap": 104, + "referenceSize": 57, + "verseCenterGaps": [ + 90, + 91 + ] + }, + "reviewState": "pending user review" + } +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/full-manifest.json b/in-progress/cards/BP-001-moses/history/revisions/v03/full-manifest.json new file mode 100644 index 0000000..985f58f --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/full-manifest.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v03", + "stage": "art-review", + "approval": null, + "content": "card.json", + "files": { + "README.md": { + "sha256": "d8174612d3d3455ad9a5ff972cba19908df343bab5d9a15d25f641be1f8fbef2" + }, + "card.json": { + "sha256": "678d59f142b18b6a589b524968c65eb06525bd6137c8de720f9090730ef74317" + }, + "source/art-fit.json": { + "sha256": "f06f3666c0add4fd52084a0998d9e48146ec84e0f04e51dddcfc5ca2aa396388" + }, + "source/art-master.png": { + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85" + }, + "source/art-native.png": { + "sha256": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157" + }, + "source/edit-prompt.txt": { + "sha256": "0bd8e3c5c40d59dd6f1f9caf9094371d0c3f52748e48a9cbd924609da1205e5a" + }, + "source/edit-reference.jpg": { + "sha256": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f" + }, + "source/fit-art.py": { + "sha256": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07" + }, + "source/generation.json": { + "sha256": "bf4cd2254dc45d51c79a5d29abe40613a9a8530fbf17a02d60448cf64a5a0e36" + }, + "source/refinement.md": { + "sha256": "ea3a4f27292be136543286d4d67d7780ceeb4fb986992f428736de749de6ff4d" + }, + "low/manifest.json": { + "sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b" + }, + "med/manifest.json": { + "sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6" + }, + "high/manifest.json": { + "sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a" + } + }, + "compatibilityAliases": [] +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/record.json b/in-progress/cards/BP-001-moses/history/revisions/v03/record.json new file mode 100644 index 0000000..e74b369 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/record.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": 1, + "kind": "compact-card-revision", + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v03", + "originalStage": "art-review", + "approval": null, + "reason": "superseded when v05 was accepted", + "originalManifest": "full-manifest.json", + "originalManifestSHA256": "b0b5443349cab2bdc9705aa388e45565ab60ed467d4c134f936b87bd274d0029", + "referenceResolution": "low", + "referenceImages": [ + "review/art-reference.png" + ], + "retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/art-reference.png b/in-progress/cards/BP-001-moses/history/revisions/v03/review/art-reference.png new file mode 100644 index 0000000..1dd29d3 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/art-reference.png differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/illustration-review.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/illustration-review.md new file mode 100644 index 0000000..6edd72e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/illustration-review.md @@ -0,0 +1,24 @@ +# Moses v03 — Legendary intricacy review + +**Art-review; approval pending.** Astra medium direction, built-in image edit of v02. Inspected the returned full native image and the saved 250×350 thumbnail. This pass answers the user’s request for greater Legendary intricacy while retaining v02’s reduction in realism. + +## Assessment + +The refinement succeeds at making selected parts more finely crafted without returning to realism or changing the governing composition. Moses’s head, lowered gaze, solemn expression, folded hands, silhouette and robe arrangement remain substantially intact. Tent/cloud/camp positions and the complete attached curtain remain stable. + +- Cloud gains nested ivory/champagne curves and secondary panes; its large bright silhouette still reads from a distance. It remains non-anthropomorphic. +- Curtain has finer tapering divisions that follow its folds, plus a narrow geometric glass border and complete patterned hem. Its visible attachment to the tent is maintained, retaining future foreground-overlap feasibility. +- Sapphire robes gain selected finer blue/turquoise planes within major folds. Warm trim has small triangular/elongated glass rhythms, stronger near the cuffs and opening. Broad blue and burgundy masses remain dominant. +- Tent lintel and supports gain small facets and lead joints. Sky light transition, mountain planes and some foreground ground paths gain secondary divisions while the camp remains simple and subordinate. +- Broad amber facial planes, expressive ivory beard locks and simplified hands are preserved. No realistic hair strands, veins or wrinkles return. Both wrists and hands remain visibly connected; no obvious new finger/anatomy defect appears. +- At thumbnail size, the bright cloud, dark tent opening and sapphire figure still establish the hierarchy. New detail reads mainly as richer material rather than a change in scene or action. + +## Tradeoffs + +This is visibly denser than v02, especially along the cloud column and lower robe. The primary/secondary lead hierarchy exists, but some new lines are still fairly strong; cloud curves and triangular trim repeat. Surface variation is also lively in sky and cloth. These are the areas to quiet if the user finds the new richness excessive. The gold trim is more noticeable, though confined to existing edges rather than spread across the scene. Face and hands remain comparatively simple, as requested. + +This is an art candidate for user review, not artwork approval. No composition, text placement, mask, finish, printing or moving-light validation was performed. + +## Provenance and retention + +The edit used only the most recent visible 900×1260 JPEG reference of v02 (`source/edit-reference.jpg`). Native-path reads remain unavailable because of the sandbox helper initialization error. Prompt, exact displayed reference, generated native bytes and hashes are retained. Fit and review resizing are deterministic via `source/fit-art.py`. v01 and v02 are preserved. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/README.md new file mode 100644 index 0000000..fea38d2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/README.md @@ -0,0 +1,28 @@ +# Moses — threshold of the Presence + +Provisional Legendary Normal composition by Astra Medium. This candidate makes the **pillar of cloud** the shared gesture between illustration and card: its crown passes in front of the name field, and its substantial lower lobes enter the verse field. The original attached curtain continues down the left edge. The panels sit behind those materials instead of advertising a specially cut panel corner. + +- [Full preview, 1000 px](composed-preview-1000.png) +- [Delivery preview, 500 px](composed-preview-500.png) +- [V2 / Astra v3 comparison](v2-v3-comparison.png) +- [Cloud interaction details](cloud-interaction-detail.png) +- [Layer-order comparison](curtain-comparison.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +## Design + +The story here is intimate access to the divine Presence at the tent threshold. The cloud supplies the luminous ivory, champagne and opal silhouette; the tent supplies the burgundy and gold material. Both belong to Moses's existing scene. A restrained gold enclosure and warm ivory fields let those illustrated forms carry the identity. No David mantle or leaf motif is borrowed. + +The verse is set in three substantial lines to the right of the cloud, preserving its exact wording without quotation marks. The text deliberately occupies the unobstructed part of the backing. The title remains MOSES; the reference remains Deuteronomy 34:10 • ESV. All lettering uses the repository P052 faces and is generated deterministically. + +## Source and scope + +The selected `../../source/art-master.png` remains unchanged, at its original 2000 × 2800 coordinates. Foreground SVG clips re-show its existing cloud and curtain above the panels. A short seven-pixel dark lead arc closes the lowest cloud lobe where the original scene's rocky ground interrupted its silhouette. This is an editable contour treatment, not regenerated source art. + +A separate generated curtain adaptation was attempted with built-in imagegen, but generation failed before reading the image because the environment's filesystem sandbox helper reported `bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted`. No generated donor exists or is used. This candidate is the completed SVG-native cloud direction. + +Run `python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v3/build-preview.py` from the repository. Inkscape, Fontconfig, Pillow and NumPy are required. The builder checks font hashes and glyph coverage, exact content, measured glyph bounds, opaque backing coverage, zero foreground/text overlap, and preservation of the source hash. Verse spacing is 77 master pixels from its upper rule and 86 pixels to the reference ink. + +Static card-size and enlarged-join inspection completed. This remains a review candidate: no production printing exports, finish masks, harness fixtures, promotion or final approval were produced. The existing v1/v2 review folders and production assets are untouched. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/build-preview.py b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/build-preview.py new file mode 100644 index 0000000..c2820c3 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/build-preview.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800') +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z' +title_inner='' +verse_inner='' +def panel(path,inner,ident):return f'{inner}' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing') +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +cloud_top='M0 0H714 Q752 43 748 83 Q805 43 852 48 C926 52 977 107 964 179 C1008 212 1010 268 983 320 Q964 353 929 370 L730 475 H0Z' +cloud_low='M0 400H352 V1780H765 L758 1985 Q750 2040 726 2108 L709 2220 L709 2314 Q695 2340 660 2362 Q651 2405 612 2434 Q579 2462 538 2473 Q507 2485 467 2488 Q401 2473 353 2410 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +curtain=''+''.join(f'' for clip in ['cloud-top','cloud-low'])+'' +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen a',1320,2305,74,'Sanctification P052',500), + ('verse-1','prophet since in Israel like Moses,',1320,2413,74,'Sanctification P052',500), + ('verse-2','whom the LORD knew face to face',1320,2521,74,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2650,57,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [730,2180,1900,2690] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2174;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=20,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'Provisional Legendary Normal — threshold of the Presence', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':108, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review', + 'generationAttempt':{'tool':'built-in imagegen','status':'failed before generation','reason':'filesystem sandbox helper: bwrap loopback Failed RTM_NEWADDR: Operation not permitted','fallback':'Native SVG contours around existing source artwork; no generated donor'}, + 'foregroundAdaptation':'Cloud extracted in original coordinates. A 7px dark SVG lead arc closes the lowest lobe across its original ground occlusion; source file unchanged.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Reproducible v2/v3 presentation and focused interaction review. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('V2 — textile edge',P.parent/'layout-preview-v2/composed-preview-1000.png'),('Astra v3 — pillar of cloud',P/'composed-preview-1000.png')]): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) +board.save(P/'v2-v3-comparison.png') +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im.thumbnail(size);detail.paste(im,origin) +im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722)) +detail.save(P/'cloud-interaction-detail.png') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/fonts.conf b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/fonts.conf new file mode 100644 index 0000000..0328966 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v3/font-cache \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/preview-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/preview-validation.json new file mode 100644 index 0000000..ccea602 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v3/preview-validation.json @@ -0,0 +1,182 @@ +{ + "status": "passed", + "scope": "Provisional Legendary Normal \u2014 threshold of the Presence", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "unchanged": true + }, + "template": { + "governingIdea": "The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M0 0H714 Q752 43 748 83 Q805 43 852 48 C926 52 977 107 964 179 C1008 212 1010 268 983 320 Q964 353 929 370 L730 475 H0Z", + "cloudLowerContour": "M0 400H352 V1780H765 L758 1985 Q750 2040 726 2108 L709 2220 L709 2314 Q695 2340 660 2362 Q651 2405 612 2434 Q579 2462 538 2473 Q507 2485 467 2488 Q401 2473 353 2410 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z", + "curtainAlphaBounds": [ + 0, + 0, + 1001, + 2647 + ], + "curtainFrameOrBackingOverlapPixels": 347899 + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a", + "baseline": 2305, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 890, + 2251, + 1749, + 2307 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "prophet since in Israel like Moses,", + "baseline": 2413, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 769, + 2359, + 1869, + 2435 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "whom the LORD knew face to face", + "baseline": 2521, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 752, + 2466, + 1886, + 2523 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2650, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2609, + 1649, + 2667 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 752, + 2251, + 1886, + 2523 + ], + "upperFlourishToVerseInk": 77, + "verseInkToReferenceLineBox": 86, + "centeringDifference": 9, + "lineBaselineGap": 108 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed" + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review", + "generationAttempt": { + "tool": "built-in imagegen", + "status": "failed before generation", + "reason": "filesystem sandbox helper: bwrap loopback Failed RTM_NEWADDR: Operation not permitted", + "fallback": "Native SVG contours around existing source artwork; no generated donor" + }, + "foregroundAdaptation": "Cloud extracted in original coordinates. A 7px dark SVG lead arc closes the lowest lobe across its original ground occlusion; source file unchanged." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/README.md new file mode 100644 index 0000000..cb95ff2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/README.md @@ -0,0 +1,24 @@ +# Moses — refined cloud foreground + +This provisional Normal candidate retains the v3 composition and refines the cloud where it crosses the name and verse backings. + +- [Full preview](composed-preview-1000.png) +- [500 px preview](composed-preview-500.png) +- [V3 / V4 card comparison](v3-v4-comparison.png) +- [V3 / V4 enlarged junction comparison](v3-v4-junction-comparison.png) +- [V4 interaction details](cloud-interaction-detail.png) +- [Editable foreground](foreground-curtain.svg) +- [Reproducible builder](build-preview.py) +- [Validation](preview-validation.json) + +The prior contour passed through existing lead seams and retained small sky/terrain slivers, producing a clipped edge. V4 traces the cloud with fewer continuous cubic Bézier segments. A consistent eight-master-pixel dark lead perimeter integrates the exposed edges with the stained-glass construction. At the lower end, an authored rounded closure replaces the broken boundary inherited from the source's rocky ground. This foreground adaptation is deliberate; it does not need to retain that original occlusion. + +The cloud interior stays registered to the original illustration. Geometry-heavy outputs render at 4000 × 5600, then downsample to the 2000 × 2800 master in premultiplied RGBA using Lanczos. The glass structure receives no blur. The full source art remains unchanged. + +Exact title, verse and reference are retained. Font hashes, glyph coverage, measured safe bounds, zero foreground/text intersection, and opaque backing under all lettering are checked. The text and frame/backing raster components must match v3 pixel for pixel. Static inspection covers full card and enlarged upper/lower joins. + +Rebuild from the repository with: + +`python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/build-preview.py` + +Requires Inkscape, Fontconfig, Pillow and NumPy. This is a provisional review composition only. V3 is preserved. No production layers, printing exports, finish masks, harness fixture, GPU review or promotion are included. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/build-preview.py b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/build-preview.py new file mode 100644 index 0000000..c687339 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/build-preview.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z' +title_inner='' +verse_inner='' +def panel(path,inner,ident):return f'{inner}' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing') +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369' +lower_edge='M736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350' +cloud_top=top_edge+' L730 475 H0 V0Z' +cloud_low='M0 400H352 V1780H765 C765 1920 748 2010 736 2080 '+lower_edge.removeprefix('M736 2080 ')+' L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top','cloud-low'])+lead+'' +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen a',1320,2305,74,'Sanctification P052',500), + ('verse-1','prophet since in Israel like Moses,',1320,2413,74,'Sanctification P052',500), + ('verse-2','whom the LORD knew face to face',1320,2521,74,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2650,57,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [730,2180,1900,2690] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2174;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=20,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'Provisional Legendary Normal — threshold of the Presence', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':108, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Reproducible v3/v4 presentation and focused interaction review. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Astra v3 — initial contours',P.parent/'layout-preview-astra-v3/composed-preview-1000.png'),('Astra v4 — refined contours',P/'composed-preview-1000.png')]): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) +board.save(P/'v3-v4-comparison.png') +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im.thumbnail(size);detail.paste(im,origin) +im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722)) +detail.save(P/'cloud-interaction-detail.png') + +# Verify the refinement has no layout/type drift from v3. +previous=P.parent/'layout-preview-astra-v3' +for filename in ['text-preview.png','border-backing-preview.png']: + assert np.array_equal(np.asarray(Image.open(P/filename)),np.asarray(Image.open(previous/filename))), filename +report['structuralChecks']['textPixelsIdenticalToV3']=True +report['structuralChecks']['frameAndBackingPixelsIdenticalToV3']=True +report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +# Matched enlarged crops make contour changes directly reviewable. +board=Image.new('RGB',(1480,1430),'#11161c');d=ImageDraw.Draw(board) +for col,(label,folder) in enumerate([('V3 — initial contour',previous),('V4 — refined lead contour',P)]): + d.text((col*740+22,18),label,font=label_font,fill='#f4ead4') + im=Image.open(folder/'composed-preview-2000.png').convert('RGB') + for box,y,size in [((680,0,1030,400),65,(690,600)),((320,2070,765,2520),725,(690,670))]: + crop=im.crop(box);crop.thumbnail(size);crop=crop.resize((round(crop.width*1.45),round(crop.height*1.45)),Image.Resampling.LANCZOS);crop.thumbnail(size);board.paste(crop,(col*740+22,y)) +board.save(P/'v3-v4-junction-comparison.png') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/fonts.conf b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/fonts.conf new file mode 100644 index 0000000..9c0f921 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/font-cache \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/preview-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/preview-validation.json new file mode 100644 index 0000000..5b5b01f --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v4/preview-validation.json @@ -0,0 +1,186 @@ +{ + "status": "passed", + "scope": "Provisional Legendary Normal \u2014 threshold of the Presence", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "unchanged": true + }, + "template": { + "governingIdea": "The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369 L730 475 H0 V0Z", + "cloudLowerContour": "M0 400H352 V1780H765 C765 1920 748 2010 736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z", + "curtainAlphaBounds": [ + 0, + 0, + 996, + 2649 + ], + "curtainFrameOrBackingOverlapPixels": 342547 + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a", + "baseline": 2305, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 890, + 2251, + 1749, + 2307 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "prophet since in Israel like Moses,", + "baseline": 2413, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 769, + 2359, + 1869, + 2435 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "whom the LORD knew face to face", + "baseline": 2521, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 752, + 2466, + 1886, + 2523 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2650, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2609, + 1649, + 2667 + ], + "safeBounds": [ + 730, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 752, + 2251, + 1886, + 2523 + ], + "upperFlourishToVerseInk": 77, + "verseInkToReferenceLineBox": 86, + "centeringDifference": 9, + "lineBaselineGap": 108 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "textPixelsIdenticalToV3": true, + "frameAndBackingPixelsIdenticalToV3": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false, + "alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo" + }, + "foregroundAdaptation": "Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/README.md new file mode 100644 index 0000000..46c6785 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/README.md @@ -0,0 +1,33 @@ +# Moses — suspended tent textile and coherent entrance + +This provisional Normal candidate preserves the v4 nameplate and cloud crown. Only the lower composition and near entrance structure are revised. + +- [Full preview](composed-preview-1000.png) +- [500 px preview](composed-preview-500.png) +- [V4 / V5 full comparison](v4-v5-comparison.png) +- [V4 / V5 lower junction comparison](v4-v5-junction-comparison.png) +- [Editable composition](composed-preview.svg) +- [Builder](build-preview.py) +- [Validation](preview-validation.json) + +## Lower textile + +The rigid plaque becomes a suspended warm linen field. Its shallow sag reveals more of Moses's robe; its left edge curves inward behind the cloud instead of running to the curtain pole as a wide blank rectangle. A fine woven hem replaces the metal plaque rules. The right corners bind to the existing frame. Its upper-left corner extends behind the cloud to a binding on the near entrance post, so the field belongs to the tent structure. + +The v4 cloud contour is retained, but the narrower backing changes the relationship: the cloud occupies and follows the textile edge rather than dangling over an expansive blank field. The text retains the exact words, font faces, sizes and horizontal alignment; verse baselines move down 55 master pixels and the reference moves down 40 pixels. Measured spacing is 66 pixels above the verse and 71 pixels between verse and reference ink. + +## Near entrance post + +A separate authored foreground jamb follows the existing curtain-side socket and vertical. Its dark outline, warm gold face and segmented joints match the structural language of the far post. It stays visible in front of the cloud and continues to the ground. Its slight lean and stronger near-side width make the entrance read as a pair of supports. A small binding ties the textile's concealed left corner to this post. + +This is an SVG-native foreground adaptation; `../../source/art-master.png` is unchanged. No donor image or source repaint is used. The approved nameplate/crown region and title glyph pixels must remain identical to v4, verified programmatically. + +## Verification and scope + +The builder verifies the source hash, exact text, shared font hashes and glyph coverage, safe glyph bounds, opaque backing beneath lettering, zero foreground/text intersection, and preservation of the nameplate/crown. Compositing remains at 2× resolution with premultiplied-alpha Lanczos downsampling. No glass-blurring filter is used. Static review includes the full card and matched lower-junction crops. + +Rebuild from the repository: + +`python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5/build-preview.py` + +Requires Inkscape, Fontconfig, Pillow and NumPy. Earlier candidates remain intact. This is a review candidate only: no production layers, printing exports, finish masks, harness fixture, GPU review or promotion. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/build-preview.py b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/build-preview.py new file mode 100644 index 0000000..603d96b --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/build-preview.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst): + # Supersample only geometry-heavy composite/foreground outputs; text measurements + # remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path. + factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1 + ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}') + if factor>1: + im=Image.open(dst).convert('RGBA') + im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst) + +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +# The panels tuck behind the original tent and the pillar of cloud. +# No alteration, retouch, rescaling or regeneration of the selected source. +title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z' +verse_path='M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z' +title_inner='' +verse_inner='''''' +def panel(path,inner,ident): + if ident=='verse-backing': + # A linen field with a woven seam, rather than a metallic plaque. + return f'{inner}' + return f'{inner}' +defs=defs.replace('','') +# Ties sit at the existing right rail and visibly support the hanging field. +ties=''+''.join(f'' for y in [2110,2700])+'' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')+ties +# Trace the visible source silhouette, including the whole cloud lower lobe. +# Upper crown is separately registered; both layers stay at source coordinates. +# Cubic contours follow the source lead, not the rough v3 clipping guesses. +# The lower closing arc ends above the old terrain occlusion. +top_edge='M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369' +lower_edge='M736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350' +cloud_top=top_edge+' L730 475 H0 V0Z' +cloud_low='M0 400H352 V1780H765 C765 1920 748 2010 736 2080 '+lower_edge.removeprefix('M736 2080 ')+' L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z' +defs=defs.replace('',f'') +# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud. +frame='''''' + +# A narrow opaque lead edge removes blue/terrain fringe without softening glass. +lead=f'' +curtain=''+''.join(f'' for clip in ['cloud-top','cloud-low'])+lead+'' +# Near entrance jamb: aligned to the source curtain-side upper socket. +# The cloud sits behind this continuous dark/gold glass structure. +defs=defs.replace('','') +post_path='M326 250 L360 250 L350 2640 L316 2637Z' +post=f'' +# Uneven joints follow the counterpart post's segmented construction. +for y in [310,493,720,944,1186,1432,1669,1915,2167,2406,2590]: + x=326-(y-250)*10/2390 + post+=f'' +post+='' +# A small woven binding holds the concealed left textile corner to the jamb. +post+='' +curtain+=post +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1458,244,142,'P052',700), + ('verse-0','And there has not arisen a',1320,2360,74,'Sanctification P052',500), + ('verse-1','prophet since in Israel like Moses,',1320,2468,74,'Sanctification P052',500), + ('verse-2','whom the LORD knew face to face',1320,2576,74,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2690,57,'Sanctification P052',500), +] +for ident,content,*_ in rows: + fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse'] + charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split() + ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset] + assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph') + +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1050,90,1905,330] if ident=='title' else [730,2275,1900,2730] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2240;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=20,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'Provisional Legendary Normal — suspended tent textile', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'cloudUpperContour':cloud_top, + 'cloudLowerContour':cloud_low, + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':108, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + 'fontGlyphCoverage':'passed', + 'fontManifestHashes':'passed', + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review', + 'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False}, + 'foregroundAdaptation':'V4 cloud and nameplate preserved. Lower linen field now curves behind the cloud along its side; shallow sag exposes more robe. Lower text moves down with exact words, faces, sizes and horizontal alignment retained. A continuous near entrance post is authored over the cloud, with the textile left corner bound to it behind the cloud.' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) + +# Reproducible v3/v4 presentation and focused interaction review. +label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26) +board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Astra v4 — rigid lower plaque',P.parent/'layout-preview-astra-v4/composed-preview-1000.png'),('Astra v5 — suspended textile',P/'composed-preview-1000.png')]): + d.text((i*750+24,18),label,font=label_font,fill='#f4ead4') + im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63)) +board.save(P/'v4-v5-comparison.png') +master=Image.open(P/'composed-preview-2000.png').convert('RGB') +detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail) +for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]: + d.text((origin[0],20),label,font=label_font,fill='#f4ead4') + im=master.crop(box);im.thumbnail(size);detail.paste(im,origin) +im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722)) +detail.save(P/'cloud-interaction-detail.png') + +# The approved nameplate and cloud crown remain pixel-identical. +previous=P.parent/'layout-preview-astra-v4' +current_image=np.asarray(Image.open(P/'composed-preview-2000.png')) +previous_image=np.asarray(Image.open(previous/'composed-preview-2000.png')) +assert np.array_equal(current_image[:200],previous_image[:200]) +assert np.array_equal(current_image[:500,600:],previous_image[:500,600:]) +assert np.array_equal(np.asarray(Image.open(P/'title-ink.png')),np.asarray(Image.open(previous/'title-ink.png'))) +report['structuralChecks']['titleAndCloudCrownRegionIdenticalToV4']=True +report['structuralChecks']['titlePixelsIdenticalToV4']=True +report['structuralChecks']['nearEntrancePostAuthored']=True +report['template']['nearEntrancePostPath']=post_path +report['template']['leftTextileAnchor']=[340,2106] +report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo' +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +# Matched lower card crops show the textile silhouette and interaction. +board=Image.new('RGB',(1480,930),'#11161c');d=ImageDraw.Draw(board) +for col,(label,folder) in enumerate([('V4 — rigid plaque',previous),('V5 — suspended textile',P)]): + d.text((col*740+22,18),label,font=label_font,fill='#f4ead4') + im=Image.open(folder/'composed-preview-2000.png').convert('RGB') + crop=im.crop((190,1940,2000,2800));crop.thumbnail((710,850));board.paste(crop,(col*740+15,75)) + detail=im.crop((340,2050,830,2790));detail.thumbnail((380,485));board.paste(detail,(col*740+185,425)) +board.save(P/'v4-v5-junction-comparison.png') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/fonts.conf b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/fonts.conf new file mode 100644 index 0000000..8b42838 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v5/font-cache \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/preview-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/preview-validation.json new file mode 100644 index 0000000..2ca000c --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-astra-v5/preview-validation.json @@ -0,0 +1,192 @@ +{ + "status": "passed", + "scope": "Provisional Legendary Normal \u2014 suspended tent textile", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "unchanged": true + }, + "template": { + "governingIdea": "Nameplate unchanged; a suspended linen field tucks behind the pillar of cloud and ties to the right frame", + "titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z", + "verseBackingPath": "M340 2106 C445 2114 550 2118 655 2140 C960 2230 1435 2260 1960 2095 L1960 2720 C1535 2782 1045 2728 580 2750 C488 2755 437 2684 453 2592 C462 2506 520 2470 574 2404 C625 2341 641 2230 655 2140 L340 2106Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "cloudUpperContour": "M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369 L730 475 H0 V0Z", + "cloudLowerContour": "M0 400H352 V1780H765 C765 1920 748 2010 736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z", + "curtainAlphaBounds": [ + 0, + 0, + 996, + 2714 + ], + "curtainFrameOrBackingOverlapPixels": 189298, + "nearEntrancePostPath": "M326 250 L360 250 L350 2640 L316 2637Z", + "leftTextileAnchor": [ + 340, + 2106 + ] + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 142, + "inkBounds": [ + 1202, + 145, + 1711, + 247 + ], + "safeBounds": [ + 1050, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a", + "baseline": 2360, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 890, + 2306, + 1749, + 2362 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "prophet since in Israel like Moses,", + "baseline": 2468, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 769, + 2414, + 1869, + 2490 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "whom the LORD knew face to face", + "baseline": 2576, + "font": "Sanctification P052", + "weight": 500, + "size": 74, + "inkBounds": [ + 752, + 2521, + 1886, + 2578 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2690, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2649, + 1649, + 2707 + ], + "safeBounds": [ + 730, + 2275, + 1900, + 2730 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 752, + 2306, + 1886, + 2578 + ], + "upperFlourishToVerseInk": 66, + "verseInkToReferenceLineBox": 71, + "centeringDifference": 5, + "lineBaselineGap": 108 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true, + "fontGlyphCoverage": "passed", + "fontManifestHashes": "passed", + "titleAndCloudCrownRegionIdenticalToV4": true, + "titlePixelsIdenticalToV4": true, + "nearEntrancePostAuthored": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review", + "edgeRendering": { + "geometry": "source-traced cubic Bezier paths", + "leadWidthMasterPixels": 8, + "renderScale": 2, + "downsample": "premultiplied RGBA LANCZOS", + "blur": false, + "alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo" + }, + "foregroundAdaptation": "V4 cloud and nameplate preserved. Lower linen field now curves behind the cloud along its side; shallow sag exposes more robe. Lower text moves down with exact words, faces, sizes and horizontal alignment retained. A continuous near entrance post is authored over the cloud, with the textile left corner bound to it behind the cloud." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/README.md new file mode 100644 index 0000000..87fb372 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/README.md @@ -0,0 +1,36 @@ +# Moses — provisional Legendary threshold composition + +This requested preview tests the selected v03 illustration with real P052 typography and a bespoke threshold-driven Normal layout. It is a composition candidate, not production assembly. + +- [Composed preview](composed-preview-1000.png) +- [500 × 700 preview](composed-preview-500.png) +- [Curtain comparison](curtain-comparison.png) +- [Enlarged curtain/backing junction](curtain-junction-detail.png) +- [Authored curtain foreground](foreground-curtain.svg) +- [Frame and backing source](border-backing-preview.svg) +- [Deterministic text source](text-preview.svg) +- [Validation](preview-validation.json) +- [Reproducible builder](build-preview.py) + +## Governing idea + +The outer frame uses narrow antique gold, lapis, ivory and tent-derived geometric bands. The upper-right plaque occupies open sky without covering Moses. The lower ivory textile backing begins below his folded hands and reserves a wide central-right field for the four-line verse. + +The complete attached burgundy curtain is selected from the original illustration with an authored full-canvas contour. It composes above the frame and backing, crossing the left perimeter and far-left backing corner while remaining visibly attached to the tent. It does not intersect the lettering. Moses stays behind the backing, avoiding a second character-mantle treatment like David. + +The narrative verse is presented without quotation marks: + +And there has not arisen
+a prophet since in Israel
+like Moses, whom the LORD
+knew face to face + +Deuteronomy 34:10 • ESV + +P052 Bold 700 renders MOSES at 138px. Sanctification P052 Medium 500 renders the verse at 80px with 94px baselines and the reference at 57px. The measured verse ink has 54 master pixels above and 53 below within its reserved band. + +## Scope + +Only the provisional Normal composition is rendered. The proposed printing behavior remains a hypothesis until this layout is accepted: Normal would use frame, backings, curtain, and text; Borderless would use backings, curtain, and text; Textless would use frame and curtain; Boundless would use the unmodified illustration. + +No production printings, finish masks, text masks, harness fixtures, moving-light review, or final approval were created. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/build-preview.py b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/build-preview.py new file mode 100644 index 0000000..eafd4e8 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/build-preview.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800') +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +title_path='M1110 66 H1888 L1938 116 V320 L1895 364 H1110 L1060 314 V116 Z' +verse_path='M205 2075 H1892 Q1942 2075 1942 2125 V2715 Q1942 2765 1892 2765 H205 Q164 2765 164 2724 V2116 Q164 2075 205 2075 Z' +title_inner='' +verse_inner='' +def panel(path,inner,ident):return f'{inner}' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing') + +curtain=f'' +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1500,244,138,'P052',700), + ('verse-0','And there has not arisen',1320,2276,80,'Sanctification P052',500), + ('verse-1','a prophet since in Israel',1320,2370,80,'Sanctification P052',500), + ('verse-2','like Moses, whom the LORD',1320,2464,80,'Sanctification P052',500), + ('verse-3','knew face to face',1320,2558,80,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1320,2655,57,'Sanctification P052',500), +] +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1100,90,1905,340] if ident=='title' else [570,2165,1900,2690] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2163;ref_top=2613;above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=4,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Backing and frame alone',P/'without-curtain-1000.png'),('Threshold curtain overlap',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Curtain over frame/backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'Provisional Legendary Normal composition only', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Tent threshold', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':94, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'pending user review' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/fonts.conf b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/fonts.conf new file mode 100644 index 0000000..e243ebc --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-v1/font-cache \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/preview-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/preview-validation.json new file mode 100644 index 0000000..51f99e5 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v1/preview-validation.json @@ -0,0 +1,193 @@ +{ + "status": "passed", + "scope": "Provisional Legendary Normal composition only", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "unchanged": true + }, + "template": { + "governingIdea": "Tent threshold", + "titleBackingPath": "M1110 66 H1888 L1938 116 V320 L1895 364 H1110 L1060 314 V116 Z", + "verseBackingPath": "M205 2075 H1892 Q1942 2075 1942 2125 V2715 Q1942 2765 1892 2765 H205 Q164 2765 164 2724 V2116 Q164 2075 205 2075 Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "curtainAlphaBounds": [ + 0, + 0, + 769, + 2800 + ], + "curtainFrameOrBackingOverlapPixels": 283408 + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 138, + "inkBounds": [ + 1251, + 148, + 1746, + 247 + ], + "safeBounds": [ + 1100, + 90, + 1905, + 340 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen", + "baseline": 2276, + "font": "Sanctification P052", + "weight": 500, + "size": 80, + "inkBounds": [ + 886, + 2217, + 1755, + 2278 + ], + "safeBounds": [ + 570, + 2165, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "a prophet since in Israel", + "baseline": 2370, + "font": "Sanctification P052", + "weight": 500, + "size": 80, + "inkBounds": [ + 902, + 2311, + 1739, + 2393 + ], + "safeBounds": [ + 570, + 2165, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "like Moses, whom the LORD", + "baseline": 2464, + "font": "Sanctification P052", + "weight": 500, + "size": 80, + "inkBounds": [ + 813, + 2405, + 1827, + 2477 + ], + "safeBounds": [ + 570, + 2165, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-3", + "text": "knew face to face", + "baseline": 2558, + "font": "Sanctification P052", + "weight": 500, + "size": 80, + "inkBounds": [ + 1019, + 2499, + 1620, + 2560 + ], + "safeBounds": [ + 570, + 2165, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2655, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 991, + 2614, + 1649, + 2672 + ], + "safeBounds": [ + 570, + 2165, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 813, + 2217, + 1827, + 2560 + ], + "upperFlourishToVerseInk": 54, + "verseInkToReferenceLineBox": 53, + "centeringDifference": 1, + "lineBaselineGap": 94 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "pending user review" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/README.md b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/README.md new file mode 100644 index 0000000..f8d9e66 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/README.md @@ -0,0 +1,36 @@ +# Moses — provisional Legendary compact textile composition + +This preview revises the selected v03 illustration with a narrower, tent-derived backing and a three-line verse setting. It is a composition candidate, not production assembly. + +- [Composed preview](composed-preview-1000.png) +- [500 × 700 preview](composed-preview-500.png) +- [v1 / v2 backing comparison](backing-comparison.png) +- [Curtain comparison](curtain-comparison.png) +- [Enlarged curtain/backing junction](curtain-junction-detail.png) +- [Authored curtain foreground](foreground-curtain.svg) +- [Frame and backing source](border-backing-preview.svg) +- [Deterministic text source](text-preview.svg) +- [Validation](preview-validation.json) +- [Reproducible builder](build-preview.py) + +## Governing idea + +The outer frame uses narrow antique gold, lapis, ivory and tent-derived geometric bands. The upper-right plaque now reads as a sloped canopy rather than a chamfered royal plaque. The lower ivory backing enters beneath the curtain, then steps inward to form a compact woven screen. Burgundy, lapis, and restrained gold bands replace the centered lozenge flourish language used on David. + +The attached burgundy curtain remains above the frame and backing. The backing no longer spans the unused lower-left bay: its visible edge follows the threshold transition, while the three-line verse expands across the usable textile field. Moses stays behind the backing. + +The narrative verse is presented without quotation marks: + +And there has not arisen a prophet since
+in Israel like Moses, whom the LORD
+knew face to face + +Deuteronomy 34:10 • ESV + +P052 Bold 700 renders MOSES at 138px. Sanctification P052 Medium 500 renders the verse at 72px with 104px baselines and the reference at 57px. Validation measures the actual verse ink between the upper textile band and the top of the reference. + +## Scope + +Only the provisional Normal composition is rendered. The proposed printing behavior remains a hypothesis until this layout is accepted: Normal would use frame, backings, curtain, and text; Borderless would use backings, curtain, and text; Textless would use frame and curtain; Boundless would use the unmodified illustration. + +No production printings, finish masks, text masks, harness fixtures, moving-light review, or final approval were created. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/build-preview.py b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/build-preview.py new file mode 100644 index 0000000..85c05ec --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/build-preview.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Provisional BP-001 Moses Legendary composition; no production assembly.""" +from pathlib import Path +import hashlib,json,os,subprocess +from xml.sax.saxutils import escape +import numpy as np +from PIL import Image,ImageDraw,ImageFont + +P=Path(__file__).resolve().parent +REV=P.parents[1] +REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file()) +ART=REV/'source/art-master.png' +FONTS=REPO/'fonts' +sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +art_hash=sha(ART) +font_manifest=json.loads((FONTS/'manifest.json').read_text()) +font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'} +for name in set(font_files.values()): + assert sha(FONTS/name)==font_manifest['files'][name]['sha256'] +(P/'font-cache').mkdir(exist_ok=True) +(P/'fonts.conf').write_text(f'{FONTS}{P/"font-cache"}') +env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) +def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE).strip() +def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800') +def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve() +assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve() +assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve() + +head='' +defs=''' + + + + + +''' +base='' +frame=''' + + + + + + + +''' +title_path='M1080 72 H1888 L1940 124 V342 H1045 V148 Z' +verse_path='M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z' +title_inner='' +verse_inner='' +def panel(path,inner,ident):return f'{inner}' +panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing') + +curtain=f'' +(P/'foreground-curtain.svg').write_text(head+defs+curtain+'');render(P/'foreground-curtain.svg',P/'foreground-curtain.png') +fg=Image.open(P/'foreground-curtain.png').convert('RGBA') +fa=np.asarray(fg.getchannel('A')) +assert fa.max()==255 and fa.min()==0 + +rows=[ + ('title','MOSES',1500,244,138,'P052',700), + ('verse-0','And there has not arisen a prophet since',1190,2308,72,'Sanctification P052',500), + ('verse-1','in Israel like Moses, whom the LORD',1190,2412,72,'Sanctification P052',500), + ('verse-2','knew face to face',1190,2516,72,'Sanctification P052',500), + ('reference','Deuteronomy 34:10 • ESV',1190,2650,57,'Sanctification P052',500), +] +texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) +(P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') +text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A')) +assert not np.any((ta>0)&(fa>0)) + +(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'');render(P/'border-backing-preview.svg',P/'border-backing-preview.png') +backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +checks=[];verse_bounds=[] +for ident,text,x,y,size,family,weight in rows: + node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' + src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'');render(src,dst) + alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs) + bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)] + safe=[1080,90,1905,330] if ident=='title' else [500,2180,1900,2690] + assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe) + assert np.all(backing[alpha>0]==255),ident + assert not np.any((alpha>0)&(fa>0)),ident + checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0}) + if ident.startswith('verse-'):verse_bounds.append(bounds) + +assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt'] +assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay'] +union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)] +upper=2165;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3] +assert min(above,below)>=35 and abs(above-below)<=4,(above,below) + +for name,include_curtain in [('composed-preview',True),('without-curtain',False)]: + body=base+frame+panels+(curtain if include_curtain else '')+texts + src=P/(name+'.svg');src.write_text(head+defs+body+'');render(src,P/(name+'-2000.png')) + master=Image.open(P/(name+'-2000.png')).convert('RGB') + master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) + master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png')) + +# Compare the structural effect of the attached curtain. +board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board) +for i,(label,path) in enumerate([('Backing and frame alone',P/'without-curtain-1000.png'),('Threshold curtain overlap',P/'composed-preview-1000.png')]): + draw.text((i*500+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45)) +board.save(P/'curtain-comparison.png') + +# A focused junction comparison at master crop. +detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail) +for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Curtain over frame/backing',P/'composed-preview-2000.png')]): + draw.text((i*600+12,12),label,fill='#f4ead4') + image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52)) +detail.save(P/'curtain-junction-detail.png') + +frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A')) +foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0))) +report={ + 'status':'passed', + 'scope':'Provisional Legendary Normal compact textile composition only', + 'canvas':[2000,2800], + 'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash}, + 'template':{ + 'governingIdea':'Tent threshold with compact woven screen', + 'titleBackingPath':title_path, + 'verseBackingPath':verse_path, + 'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'], + 'curtainContour':'foreground-curtain.svg', + 'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()), + 'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap, + }, + 'typography':{ + 'fontManifestSHA256':sha(FONTS/'manifest.json'), + 'checks':checks, + 'verseInkUnion':union, + 'upperFlourishToVerseInk':above, + 'verseInkToReferenceLineBox':below, + 'centeringDifference':abs(above-below), + 'lineBaselineGap':104, + }, + 'structuralChecks':{ + 'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))), + 'glyphPixelsOutsideOpaqueBacking':0, + 'artworkSHA256Unchanged':True, + }, + 'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'], + 'staticReview':'pending user review' +} +(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') +assert sha(ART)==art_hash +print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2)) diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 new file mode 100644 index 0000000..9b3c883 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 new file mode 100644 index 0000000..ab3ea61 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 new file mode 100644 index 0000000..d396faa Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 new file mode 100644 index 0000000..b056420 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9 differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/fonts.conf b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/fonts.conf new file mode 100644 index 0000000..fcdd402 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/fonts.conf @@ -0,0 +1 @@ +/home/dkzver/docker/sanctification/fonts/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-v2/font-cache \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/preview-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/preview-validation.json new file mode 100644 index 0000000..12e110f --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/review/layout-preview-v2/preview-validation.json @@ -0,0 +1,171 @@ +{ + "status": "passed", + "scope": "Provisional Legendary Normal compact textile composition only", + "canvas": [ + 2000, + 2800 + ], + "sourceArtwork": { + "path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png", + "sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "unchanged": true + }, + "template": { + "governingIdea": "Tent threshold with compact woven screen", + "titleBackingPath": "M1080 72 H1888 L1940 124 V342 H1045 V148 Z", + "verseBackingPath": "M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z", + "compositionOrder": [ + "base-art", + "frame-and-backings", + "attached-curtain-foreground", + "deterministic-text" + ], + "curtainContour": "foreground-curtain.svg", + "curtainAlphaBounds": [ + 0, + 0, + 769, + 2800 + ], + "curtainFrameOrBackingOverlapPixels": 181809 + }, + "typography": { + "fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178", + "checks": [ + { + "id": "title", + "text": "MOSES", + "baseline": 244, + "font": "P052", + "weight": 700, + "size": 138, + "inkBounds": [ + 1251, + 148, + 1746, + 247 + ], + "safeBounds": [ + 1080, + 90, + 1905, + 330 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-0", + "text": "And there has not arisen a prophet since", + "baseline": 2308, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 548, + 2255, + 1831, + 2329 + ], + "safeBounds": [ + 500, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-1", + "text": "in Israel like Moses, whom the LORD", + "baseline": 2412, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 598, + 2359, + 1782, + 2424 + ], + "safeBounds": [ + 500, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "verse-2", + "text": "knew face to face", + "baseline": 2516, + "font": "Sanctification P052", + "weight": 500, + "size": 72, + "inkBounds": [ + 919, + 2463, + 1460, + 2518 + ], + "safeBounds": [ + 500, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + }, + { + "id": "reference", + "text": "Deuteronomy 34:10 \u2022 ESV", + "baseline": 2650, + "font": "Sanctification P052", + "weight": 500, + "size": 57, + "inkBounds": [ + 861, + 2609, + 1519, + 2667 + ], + "safeBounds": [ + 500, + 2180, + 1900, + 2690 + ], + "glyphPixelsOutsideOpaqueBacking": 0, + "foregroundIntersectionPixels": 0 + } + ], + "verseInkUnion": [ + 548, + 2255, + 1831, + 2518 + ], + "upperFlourishToVerseInk": 90, + "verseInkToReferenceLineBox": 91, + "centeringDifference": 1, + "lineBaselineGap": 104 + }, + "structuralChecks": { + "curtainTextIntersectionPixels": 0, + "glyphPixelsOutsideOpaqueBacking": 0, + "artworkSHA256Unchanged": true + }, + "notPerformed": [ + "Borderless/Textless/Boundless composition", + "Production finish masks", + "Production text masks", + "Harness fixture installation", + "GPU moving-light validation", + "Final card approval" + ], + "staticReview": "pending user review" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/source/art-fit.json b/in-progress/cards/BP-001-moses/history/revisions/v03/source/art-fit.json new file mode 100644 index 0000000..40d37c7 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/source/art-fit.json @@ -0,0 +1,24 @@ +{ + "source": "art-native.png", + "sourceDimensions": [ + 1060, + 1484 + ], + "canvas": [ + 2000, + 2800 + ], + "method": "uniform centered cover fit", + "scale": 1.8867924528301887, + "scaledDimensions": [ + 2000.0, + 2800.0 + ], + "cropOffset": [ + 0.0, + 0.0 + ], + "resampling": "Pillow LANCZOS", + "sourceSHA256": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157", + "outputSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/source/edit-prompt.txt b/in-progress/cards/BP-001-moses/history/revisions/v03/source/edit-prompt.txt new file mode 100644 index 0000000..546402d --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/source/edit-prompt.txt @@ -0,0 +1,16 @@ +Use case: style-transfer +Asset type: targeted v03 refinement of the supplied Moses stained-glass illustration for a Legendary Sanctification card. +Primary request: Keep this exact composition and its decisive non-realistic glass language, but make the craftsmanship richer and more intricate in carefully chosen regions. Simple and iconic at thumbnail size, finely worked and jewel-like up close. This is an EDIT of this image, not a new composition. + +Strict invariants: preserve Moses's position, silhouette, size, left-facing lowered gaze, solemn expression, broad stylized amber facial planes, expressive ivory beard locks, calm posture and exact folded-hand gesture/anatomy/ownership. Preserve face and hands as broad legible planes, never realistic wrinkles, veins, pores, individual hair strands, or smooth anatomical gradients. Keep tent, attached hanging burgundy curtain and its full hem, cloud, camp, mountain horizon, robe colors and folds, and overall framing in the same positions. Curtain must stay visibly connected to its tent attachment for a future overlap. Keep non-anthropomorphic cloud and warm cloud-light/cool landscape relationship. Keep full-bleed 5:7. + +Refinement: Introduce a clear hierarchy of primary structural leading and thinner secondary leading. Existing large pane silhouettes remain the composition's foundation. Selectively subdivide some of them into beautifully designed curving, tapered and angular smaller glass pieces which follow the forms. Do not simply add a uniform crack network. +1. Cloud at tent entrance and upper left: the main broad lobed forms stay readable, with interlocking smaller ivory, champagne, pale amber and cool opal panes nested selectively along bright transitions and curled edges. Some large quiet glowing pieces remain. This should feel skillfully assembled from precious glass, not cottony realism, glitter or repeated tiny tiles. +2. Attached burgundy curtain: preserve its long substantial drape and dark fold valleys. Add fine flowing secondary pane divisions following chosen folds, and restrained inset glass patterning along a narrow edge/hem band. Patterns belong to the cloth's construction; no new symbols, words or ornate fantasy brocade. Keep broad wine-colored rests between detailed passages. +3. Moses's robe: preserve the broad sapphire and burgundy masses. Refine select major fold ridges into nested elongated jewel panes and subtly contrasting blue/turquoise sub-panes. Add a restrained small geometric glass rhythm to the existing warm trim near sleeves and robe opening, sparse and subordinate. Face, beard and hands remain simpler than the cloth, retaining their present stylization. +4. Tent structure: finer lead joints and small warm glass facets around the existing supports and upper angled cloth edge, without changing architecture or adding decoration that competes with the figure. +5. Sky and mountains: selected thin layered panes at the gold-to-blue light transition and a few thoughtfully split violet mountain planes, while most sky remains broad and restful. No extra rays, halos or sun symbols. +6. Camp and terrain: keep simplified tent silhouettes and grouped vegetation. Add modest secondary pane rhythms to a few tent folds and stepping ground planes near the foreground, not realistic tiny life or extra objects. Keep distant camp quieter than Moses. + +Material: jewel-colored worked glass, delicate pane-contained brushed/etched satin texture, richer selective bounded color transitions and purposeful form-following highlights. Occasional fine painted-glass accents within panes, not photorealistic shading. Craft detail rather than all-over grain. Gold is restrained warm glass trim, not metallic decoration everywhere. +Avoid: same shard size/density throughout, uniform tessellation, micro-mosaic, random cracks, omnipresent speckles or sparkles, noisy ornament, gaudy gold, realistic face/hands/landscape, clutter, altered pose, altered hand anatomy, new props or narrative events. No text, border, card frame, backing panels, watermark or literal divine figure. \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/source/fit-art.py b/in-progress/cards/BP-001-moses/history/revisions/v03/source/fit-art.py new file mode 100644 index 0000000..87eaac2 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/source/fit-art.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Reproduce only the fitted art and review sizes; never assemble a card.""" +from pathlib import Path +from PIL import Image, ImageOps +import hashlib,json +root=Path(__file__).resolve().parent.parent +native=root/'source/art-native.png' +with Image.open(native) as source: + im=source.convert('RGB');w,h=im.size + scale=max(2000/w,2800/h) + art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5)) + art.save(root/'source/art-master.png') + for name,size in [('art-review.png',(1000,1400)),('art-thumbnail.png',(250,350))]: + art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name) +fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()} +(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/source/generation.json b/in-progress/cards/BP-001-moses/history/revisions/v03/source/generation.json new file mode 100644 index 0000000..95939f4 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/source/generation.json @@ -0,0 +1,16 @@ +{ + "mode": "built-in imagegen edit", + "artDirectionModel": "gpt-6-astra", + "artDirectionReasoning": "medium", + "prompt": "edit-prompt.txt", + "native": "art-native.png", + "originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-1638b62b-56e6-4a1a-8198-8420dfb9f761.png", + "reference": "edit-reference.jpg", + "referenceSHA256": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f", + "referenceProvenance": "900x1260 JPEG quality 90 made from v02 source/art-native.png and shown in conversation; native-path tool reads unavailable due to sandbox initialization failure, so num_last_images_to_include=1 was used.", + "priorNative": "../../v02/source/art-native.png", + "priorNativeSHA256": "145d11849d97e9007fb4066b167b1bfa53e608aeb025ef8783ab1a79fc59f71d", + "fit": "art-fit.json", + "generationReproducibility": "Stochastic targeted edit; exact prompt, actual displayed reference and native output retained; fit and review sizes deterministic.", + "approval": null +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v03/source/refinement.md b/in-progress/cards/BP-001-moses/history/revisions/v03/source/refinement.md new file mode 100644 index 0000000..b19132a --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v03/source/refinement.md @@ -0,0 +1,5 @@ +# Moses v03 — curated Legendary intricacy + +The user requested greater intricacy for Legendary presentation after the v02 realism reduction. Retain the exact governing composition and broad stylized face, hands and beard. Add form-following secondary leading and finer nested glass shapes selectively in the cloud, attached curtain, robe folds/trim, tent joints, sky transition and some terrain rhythms. Retain large quiet regions and camp subordination. + +Astra medium authored the edit. Native-path image input is unavailable due to sandbox-helper initialization failures, so the successful conversation-image path uses a retained 900×1260 JPEG quality 90 made from v02 native output, shown immediately before the edit call. No other image was included. Earlier revisions remain intact. No assembly assets are generated. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/README.md b/in-progress/cards/BP-001-moses/history/revisions/v04/README.md new file mode 100644 index 0000000..85ca1b8 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/README.md @@ -0,0 +1,16 @@ +# BP-001 — Moses — v04 + +**The Threshold — continuous cloud/presence.** Astra medium direction; built-in targeted image edit from v03’s master. The cloud changes from golden circular cells to rising pearl/ivory layers with cool internal shadows and restrained warm edges. Pending user artwork review. + +- `review/art-card-size.png`: 500×700 artwork review. +- `review/art-review.png`: 1000×1400 artwork review. +- `review/illustration-review.md`: visual assessment and tradeoffs. +- `review/source-validation.json`: source, fit and v03 preservation checks. +- `source/art-native.png`: unchanged generated output. +- `source/art-master.png`: fitted 2000×2800 illustration. +- `source/edit-prompt.txt`: exact targeted prompt. +- `source/edit-reference.jpg`: actual edit reference derived from v03 master. +- `source/generation.json`: source/provenance and hashes. +- `source/art-fit.json`: uniform fit record. + +Reproduce review images with `python3 source/fit-art.py`; verify with `python3 source/validate-art.py`. Native-path input failed sandbox initialization, so the edit used the retained conversation-visible reference derived from v03 master. No new card assembly assets were generated; v03 remains unchanged. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/card.json b/in-progress/cards/BP-001-moses/history/revisions/v04/card.json new file mode 100644 index 0000000..ebe0975 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/card.json @@ -0,0 +1,27 @@ +{ + "cardId": "BP-001", + "title": "Moses", + "name": "MOSES", + "rarity": "Legendary", + "folderName": "BP-001-moses", + "artApproval": null, + "reference": "Deuteronomy 34:10", + "translation": "ESV", + "referenceDisplay": "Deuteronomy 34:10 \u2022 ESV", + "excerpt": "And there has not arisen a prophet since in Israel like Moses, whom the LORD knew face to face", + "direction": "The Threshold", + "artStatus": "Provisional v04 cloud/presence refinement awaiting review", + "layoutStatus": "Art-only v04 cloud refinement. Prior v03 provisional layouts are preserved in v03; no layout rebuilt for this revision.", + "sceneConstraints": [ + "Outside the tent of meeting after speaking with the Lord; divine cloud by the tent; Israelite camp recedes behind.", + "Calm, holy, weight-bearing; reverent intimacy, solemn responsibility, quiet authority, sacred awe.", + "No Red Sea or Sinai action; these belong to event cards." + ], + "compositionApproval": { + "by": "user", + "scope": "v01 composition only; visual rendering requested to change" + }, + "revisionIntent": "Edit only the cloud/presence to read as a continuous atmospheric pillar, not golden bubbles; preserve v03 composition and glass style.", + "art": "in-progress/cards/BP-001-moses/revisions/v04/source/art-master.png", + "artSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/full-manifest.json b/in-progress/cards/BP-001-moses/history/revisions/v04/full-manifest.json new file mode 100644 index 0000000..84f0c19 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/full-manifest.json @@ -0,0 +1,57 @@ +{ + "schemaVersion": 1, + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v04", + "stage": "art-review", + "approval": null, + "content": "card.json", + "files": { + "README.md": { + "sha256": "311a75b80ae042313a8479d295715ea6a1d12406964aad89537dc0b1d1ce0e1f" + }, + "card.json": { + "sha256": "e10b36169c7e5c682c5e8741ee05340e8250fa3ff5d961f53546a28eb9fbc1f7" + }, + "source/art-fit.json": { + "sha256": "c945ae1717313d1f83f94e7ddd563c05c63529fe3b0dd0d4e76f4d99f0bf2d0f" + }, + "source/art-master.png": { + "sha256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e" + }, + "source/art-native.png": { + "sha256": "36f6b3ab7360b7e3d8eaf93ce1dd9edd259dfc77424ca1ef4ac729cfa126ade6" + }, + "source/edit-prompt.txt": { + "sha256": "bd590461332bf9afec442d46ebef829978a64fd51c1a76074b140bd177cd56d9" + }, + "source/edit-reference.jpg": { + "sha256": "29abc4ff23b6badc4a321b067735a4bed2eb31666759e4d12a35e4bdde69fcaa" + }, + "source/fit-art.py": { + "sha256": "ae5418915f2bcfb464714b1898a9fdfa86e4117f2de226a01260be1fd841c611" + }, + "source/generation.json": { + "sha256": "f6b2c5d8158aa8ef03544cf61eae75e4f4f120bf7f330e2b813f014a612a2c2a" + }, + "source/prior-revision-hashes.json": { + "sha256": "435f9b4a7af82e53bec288fc74f7853e2cf4039b3509e4f5a6fcbf9e4a0df9b8" + }, + "source/refinement.md": { + "sha256": "685f8e1f1875caa43fa9d1267835d096b02aa83ced550a28e8fa387fc3cd2c2e" + }, + "source/validate-art.py": { + "sha256": "d6ccbe387f1dc91eac5b4c1753cdc0d475f34527530a915c68c6905dc07bd6a3" + }, + "low/manifest.json": { + "sha256": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b" + }, + "med/manifest.json": { + "sha256": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6" + }, + "high/manifest.json": { + "sha256": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a" + } + }, + "compatibilityAliases": [] +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/record.json b/in-progress/cards/BP-001-moses/history/revisions/v04/record.json new file mode 100644 index 0000000..5af2f4e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/record.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": 1, + "kind": "compact-card-revision", + "cardId": "BP-001", + "folderName": "BP-001-moses", + "revision": "v04", + "originalStage": "art-review", + "approval": null, + "reason": "superseded when v05 was accepted", + "originalManifest": "full-manifest.json", + "originalManifestSHA256": "c054d46d8ac354ee24170b42301e3698056a70f385be2cb2386e6f2f461fc78a", + "referenceResolution": "low", + "referenceImages": [ + "review/art-reference.png" + ], + "retention": "Text provenance plus one 500 x 700 card face per available printing; full current acceptance lives in artifacts/cards." +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/review/art-reference.png b/in-progress/cards/BP-001-moses/history/revisions/v04/review/art-reference.png new file mode 100644 index 0000000..b93dd85 Binary files /dev/null and b/in-progress/cards/BP-001-moses/history/revisions/v04/review/art-reference.png differ diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/review/illustration-review.md b/in-progress/cards/BP-001-moses/history/revisions/v04/review/illustration-review.md new file mode 100644 index 0000000..7b01dfc --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/review/illustration-review.md @@ -0,0 +1,13 @@ +# Moses v04 — cloud/presence review + +**Art-review, not approved.** Astra medium directed a targeted built-in edit from the v03 master. Inspected the generated full image and saved 500×700 card-size artwork. + +The bubble/orb reading is substantially reduced. The cloud is now an upward-connected atmospheric column made of irregular overlapping billows, elongated pale channels and cool blue-gray/lavender folds. Pearl and ivory occupy most of the body; warm champagne light sits along selected edges. The column continues above the tent roof and exits the upper edge, clarifying its vertical continuity. No literal divine figure appears. + +The cloud retains stained-glass boundaries and pane-contained worked texture rather than becoming photographic vapor. Leading inside the cloud is gentler than the main structural outlines. Nested circular cells have largely been replaced by extended forms. Some rounded scalloped billow edges remain, especially above the roof, but they now join continuous layers rather than closed golden orbs. At card size the cool internal shadows make the cloud read as one luminous presence. Its visual luminosity is calmer and less gold-dominant than v03. + +Moses’s face, lowered gaze, pose, stylized beard, folded hands, robe trim and silhouette appear preserved. Tent supports, entrance geometry, attached curtain/hem and camp remain visually stable. Generative editing can shift small details; non-cloud pixel identity is not claimed. The edit slightly expands some upper cloud contours and changes internal density within the original general footprint. Curtain-overlap feasibility is retained. + +Tradeoff: the new cloud has broader, softer internal divisions and somewhat less small-pane intricacy than v03’s circular construction. This is useful for atmospheric continuity but should be judged with the user before any later assembly. No mask can substitute for this illustration decision. + +No new card frame, backing, lettering, printing, finish mask or runtime fixture was generated. Existing v03 files were hash-checked unchanged. Source validation verifies native/master hashes, RGB dimensions and byte-identical deterministic fitting/review exports; it does not certify final card composition or GPU behavior. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/review/source-validation.json b/in-progress/cards/BP-001-moses/history/revisions/v04/review/source-validation.json new file mode 100644 index 0000000..5da6629 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/review/source-validation.json @@ -0,0 +1,28 @@ +{ + "status": "pass", + "scope": "Art source/fit/review only; no card assembly or material validation", + "priorRevision": "v03", + "priorFilesUnchanged": 200, + "nativeSourceHashVerified": true, + "masterHashVerified": true, + "reviewDimensions": { + "source/art-master.png": [ + 2000, + 2800 + ], + "review/art-review.png": [ + 1000, + 1400 + ], + "review/art-card-size.png": [ + 500, + 700 + ], + "review/art-thumbnail.png": [ + 250, + 350 + ] + }, + "repeatableFitAndReviews": true, + "nonCloudPixelIdentity": "Not claimed; visually reviewed generative edit" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/art-fit.json b/in-progress/cards/BP-001-moses/history/revisions/v04/source/art-fit.json new file mode 100644 index 0000000..e6ef165 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/art-fit.json @@ -0,0 +1,24 @@ +{ + "source": "art-native.png", + "sourceDimensions": [ + 1060, + 1484 + ], + "canvas": [ + 2000, + 2800 + ], + "method": "uniform centered cover fit", + "scale": 1.8867924528301887, + "scaledDimensions": [ + 2000.0, + 2800.0 + ], + "cropOffset": [ + 0.0, + 0.0 + ], + "resampling": "Pillow LANCZOS", + "sourceSHA256": "36f6b3ab7360b7e3d8eaf93ce1dd9edd259dfc77424ca1ef4ac729cfa126ade6", + "outputSHA256": "da9624d7f5c897fbcaaada9774b2fa8c11db65288b7885040e17ad16a71d925e" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/edit-prompt.txt b/in-progress/cards/BP-001-moses/history/revisions/v04/source/edit-prompt.txt new file mode 100644 index 0000000..b801f6e --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/edit-prompt.txt @@ -0,0 +1,11 @@ +Use case: precise-object-edit +Asset type: Moses Legendary BP-001 v04 full-bleed illustration. +Edit target: the supplied v03 Moses art. Change ONLY the divine cloud/presence, currently the column of bright gold circular/scalloped shapes within the tent doorway at left and its connected continuation above the sloping tent roof. Preserve everything outside that cloud as closely as possible. + +Problem to fix: the existing cloud reads as stacked golden bubbles, coins or glowing orbs instead of the divine Presence. Redesign it unmistakably as ONE continuous atmospheric pillar of cloud, rising from the threshold through the doorway and continuing upward beyond the roof and top edge. + +New cloud design: irregular layered billows connected by long drifting, rising forms. Tall overlapping ivory and pearl ribbons of vapor, broad asymmetric bulges, slender branching wisps and elongated interlocking shapes. Varied scale and direction within a coherent upward movement. The overall existing cloud footprint stays in its current position beside Moses and behind the unchanged tent structure, but inside that footprint replace ALL repeated circles, nested rings, regular scallops and stacks of rounded cells. Its structure should resemble a continuous rising cloud bank, not beadwork, rosettes, cotton balls or discrete objects. Keep a luminous pearl/ivory body with clearly visible cool blue-gray and subtle lavender inner folds/shadows. Restrained pale champagne and warm gold only as thin localized illuminated rims or selected warm edges, NOT solid gold cloud bodies. Sacred radiance and atmospheric depth through color hierarchy inside bounded glass shapes, not neon or realistic smoke. Keep the cloud brighter than its cool internal shadows but do not make every cell glow as an individual light bulb. + +Medium: exactly the established richly crafted stained-glass language. Meaningful lead divisions trace sweeping vapor layers and irregular atmospheric forms; thinner secondary leading selectively articulates the long cloud shapes. Large quiet panes mixed with a few intricate smaller shapes, pane-contained brushed/etched glass texture and subtle translucent color shifts. Glass construction stays visible. No photographic cloud pasted into the image, no vague airbrushed fog, no random cracks, no uniform tiny tessellation. Preserve the current Legendary intricacy in the rest of the art. + +Strict invariants: Moses's face, expression, head angle, broad stylized skin planes, beard, hair, pose, folded hands and anatomy, robes and trim are unchanged. Tent poles, sloping roof/lintel, dark entrance geometry, attached burgundy curtain, its complete patterned hem and connection to the tent remain unchanged. Preserve camp, people, animal, vegetation, terrain, mountains, sky outside the specific cloud, framing, scale, palette and all existing non-cloud detail. Do not add props, symbols or other figures. Preserve 5:7 aspect and full-bleed art. No lettering, card frame, backing, watermark, literal divine figure or face, halo, lightning, particles or orbs. \ No newline at end of file diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/fit-art.py b/in-progress/cards/BP-001-moses/history/revisions/v04/source/fit-art.py new file mode 100644 index 0000000..78ab3b5 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/fit-art.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Reproduce only the fitted art and review sizes; never assemble a card.""" +from pathlib import Path +from PIL import Image, ImageOps +import hashlib,json +root=Path(__file__).resolve().parent.parent +native=root/'source/art-native.png' +with Image.open(native) as source: + im=source.convert('RGB');w,h=im.size + scale=max(2000/w,2800/h) + art=ImageOps.fit(im,(2000,2800),Image.Resampling.LANCZOS,centering=(.5,.5)) + art.save(root/'source/art-master.png') + for name,size in [('art-review.png',(1000,1400)),('art-card-size.png',(500,700)),('art-thumbnail.png',(250,350))]: + art.resize(size,Image.Resampling.LANCZOS).save(root/'review'/name) +fit={'source':'art-native.png','sourceDimensions':[w,h],'canvas':[2000,2800],'method':'uniform centered cover fit','scale':scale,'scaledDimensions':[w*scale,h*scale],'cropOffset':[(w*scale-2000)/2,(h*scale-2800)/2],'resampling':'Pillow LANCZOS','sourceSHA256':hashlib.sha256(native.read_bytes()).hexdigest(),'outputSHA256':hashlib.sha256((root/'source/art-master.png').read_bytes()).hexdigest()} +(root/'source/art-fit.json').write_text(json.dumps(fit,indent=2)+'\n') diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/generation.json b/in-progress/cards/BP-001-moses/history/revisions/v04/source/generation.json new file mode 100644 index 0000000..59507d6 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/generation.json @@ -0,0 +1,17 @@ +{ + "mode": "built-in imagegen edit", + "artDirectionModel": "gpt-6-astra", + "artDirectionReasoning": "medium", + "prompt": "edit-prompt.txt", + "native": "art-native.png", + "originalGeneratedPath": "/home/dkzver/.codex/generated_images/01a0acd6-0b11-7932-a7f3-c69f609e9974/exec-51715a57-b0ec-4350-b0dc-24a69fb429d8.png", + "reference": "edit-reference.jpg", + "referenceSHA256": "29abc4ff23b6badc4a321b067735a4bed2eb31666759e4d12a35e4bdde69fcaa", + "referenceProvenance": "1000x1400 JPEG quality 93 derived from v03 source/art-master.png and shown in conversation. Native-path image reference failed sandbox initialization; successful edit used num_last_images_to_include=1.", + "priorMaster": "../../v03/source/art-master.png", + "priorMasterSHA256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "fit": "art-fit.json", + "scope": "Cloud/presence region only requested; non-cloud preservation assessed visually, not asserted pixel-identical.", + "generationReproducibility": "Stochastic targeted edit; exact prompt, displayed reference and native bytes retained; deterministic fit and review sizing.", + "approval": null +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/prior-revision-hashes.json b/in-progress/cards/BP-001-moses/history/revisions/v04/source/prior-revision-hashes.json new file mode 100644 index 0000000..e2efde5 --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/prior-revision-hashes.json @@ -0,0 +1,202 @@ +{ + "README.md": "d8174612d3d3455ad9a5ff972cba19908df343bab5d9a15d25f641be1f8fbef2", + "card.json": "5aeddc9b7ad2412cf7d5785fa728e3158926c059fc3819e6259d273663a47335", + "high/manifest.json": "7ec683bc1aaf3a6c3d941a9011c97c5681bd1f18a21a1ea359745f092bcf2b6a", + "low/manifest.json": "c6b4ddddd22832e4ea6e2201c12a90cf4a7a1d25594b5878f12c23f22987261b", + "manifest.json": "b0b5443349cab2bdc9705aa388e45565ab60ed467d4c134f936b87bd274d0029", + "med/manifest.json": "e9cd197e91ccc2477ec18ccd62d903a29b166bc23b431dad8aa25ca60ef59ac6", + "review/art-review.png": "395f2e1fb10fe749bb9424aac0e043616dcfe42f3f6448aa2db7fd543c160cc9", + "review/art-thumbnail.png": "680317bf953d5f3b984e85c968388d03f710e0d82dd02a9cc1a815b7b769b38a", + "review/illustration-review.md": "c1399a3838afa2db0d0ce83940f56e365509df5c2eb81c35a166467821fa9ca7", + "review/layout-preview-astra-v3/README.md": "032739b971442f011bfb494be3481db906607e73899854c29d71a8f24b072018", + "review/layout-preview-astra-v3/border-backing-preview.png": "0c7960dbf90c534c10921b1234e84f55640f0f58a5ce528e15e174d51932f32e", + "review/layout-preview-astra-v3/border-backing-preview.svg": "0bf3c3be6e26ac656f5dff652d79c65d8b410dc19a03dba047c8b51da6167f9c", + "review/layout-preview-astra-v3/build-preview.py": "9026f5e67e1264f3e6d819ab0c9b4f613394f8d8ab327b5c3f3c9fb4e7099aa4", + "review/layout-preview-astra-v3/cloud-interaction-detail.png": "2f27955d889e1d102b4c8681a206e8bfd2b61e66c12e34d0b36dd605558399aa", + "review/layout-preview-astra-v3/composed-preview-1000.png": "1e0a21d705b43b1fdc8eeff36d01bef39b2ae074b98b0a4610cf30ef023df3c0", + "review/layout-preview-astra-v3/composed-preview-2000.png": "15dcac4fc1bee145de7ae2b4d3fc8c95b771720a40fd8b766419d13f687aa836", + "review/layout-preview-astra-v3/composed-preview-500.png": "35ec98353db0e2607637bdd3929fad6b0818f7ce4a54e1400caf21f569112378", + "review/layout-preview-astra-v3/composed-preview.svg": "3abac9c5f7e3bf9c9906bf7a654a989739fbc01fbfe27f4ce4707a31ed5d441b", + "review/layout-preview-astra-v3/curtain-comparison.png": "223c294bc572e53f4ddc95266a945cc4a471c0c1e88cb6f2fa2cf50af4b6f631", + "review/layout-preview-astra-v3/curtain-junction-detail.png": "6fae788bca47afb0cbdaf52b76f269c14e8190bdd05ae5fd9020a19b84831633", + "review/layout-preview-astra-v3/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130", + "review/layout-preview-astra-v3/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c", + "review/layout-preview-astra-v3/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264", + "review/layout-preview-astra-v3/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260", + "review/layout-preview-astra-v3/fonts.conf": "64384d173deefb1043c4d62cbff3dec729d57e5066fbf21923148477340b3a6d", + "review/layout-preview-astra-v3/foreground-curtain.png": "1bcb6335dc4ce58f7be024595dcb4e95a636f00bbe49e8484a77753529d1c542", + "review/layout-preview-astra-v3/foreground-curtain.svg": "ce49f4db7c621484ca7659057fa8c82d9af2aae9fffc79e47525b6c45cfe3ba3", + "review/layout-preview-astra-v3/preview-validation.json": "2af1b6de742a24783e5d14ab673da748a502de871532077517897c43859a163c", + "review/layout-preview-astra-v3/reference-ink.png": "ad1d595d08725d0ea31e09d25b6b8084581fb10a9baca170fcb2b4b9c6c8579f", + "review/layout-preview-astra-v3/reference-ink.svg": "362bd19e588158d2e67ac3d9adb6ed5ff2b4e7cd64e9809c1d52af6d1bdaa41a", + "review/layout-preview-astra-v3/text-preview.png": "f2bde9899bf80b2a904dfad69cacca55070110db348b8664e428bf52152b8c45", + "review/layout-preview-astra-v3/text-preview.svg": "51f3a5f2bfed5f8ee6fdede5818a8f0d7afa954773ade715d38252bf37a746e0", + "review/layout-preview-astra-v3/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa", + "review/layout-preview-astra-v3/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee", + "review/layout-preview-astra-v3/v2-v3-comparison.png": "262f8ae4004f9e4c85f7612a0c05802723451640608b07dc70e58ccddc19344e", + "review/layout-preview-astra-v3/verse-0-ink.png": "52691dbf58024400d4c9b833b5462fead3e591bfe42f5d6b4724899948837989", + "review/layout-preview-astra-v3/verse-0-ink.svg": "5f765f4a3737357d1dc729765c605156d25db7b2e61db41662a3973e6114c850", + "review/layout-preview-astra-v3/verse-1-ink.png": "8e2ea55c3dc5abede57b7e0b08c6687ff092d65763676ff28561fe9070c5ce2d", + "review/layout-preview-astra-v3/verse-1-ink.svg": "35fbf3657b3e2843bd590b66d8465eb6d2668f48603c28fcabb035e293cf1d33", + "review/layout-preview-astra-v3/verse-2-ink.png": "ea5aacebd89f9c6dd9fe75da842a362a64b2dbdaba3d833e86458fd81ae34f4a", + "review/layout-preview-astra-v3/verse-2-ink.svg": "0db7333d35aeb8c428f08b126dd8e0f8b7c4f963f39a2176dc6015081d4f3782", + "review/layout-preview-astra-v3/without-curtain-1000.png": "a42b0eada050563e56395f891b6dd3517b595223450f963c8e472d0ce16f1f44", + "review/layout-preview-astra-v3/without-curtain-2000.png": "6af2d52a3a2dc6bae5a2731b90b4f10221a26745f608beb30f25b01237f5beec", + "review/layout-preview-astra-v3/without-curtain-500.png": "cc157de6537e7a9055330c0a4d391c59d2b0924c1ff94061d69cf68a989953bc", + "review/layout-preview-astra-v3/without-curtain.svg": "fbd8c3598c547950c08730d52ef9d6b9f4af0f6107be121252001c47a4e896b0", + "review/layout-preview-astra-v4/README.md": "01b3499895bcb9c7a459ea47c5d7900d1d0eb7ced43764607cca4914690e9da6", + "review/layout-preview-astra-v4/border-backing-preview.png": "0c7960dbf90c534c10921b1234e84f55640f0f58a5ce528e15e174d51932f32e", + "review/layout-preview-astra-v4/border-backing-preview.svg": "ae3fcf414b48023dab4f12617c979ca85aeb6b453193697be1ac358867e8aa07", + "review/layout-preview-astra-v4/build-preview.py": "5720b67bc0a41ecc7be3e214e047764c6dca83ee2299c67f03d9e4709591cc4d", + "review/layout-preview-astra-v4/cloud-interaction-detail.png": "e4fd42aa95a28b4757d12449875cbae9cbf38d86810f3605390db308ec2026b1", + "review/layout-preview-astra-v4/composed-preview-1000.png": "5985c8945a053d76c6db9ce61d609eac8cd4c5610b2f8026809eb15bf6827828", + "review/layout-preview-astra-v4/composed-preview-2000.png": "e8cdec18a358f38a806cb83fb3042c1dba77a559701be2bf5bc07f1ea7414796", + "review/layout-preview-astra-v4/composed-preview-500.png": "f59509cec2b7d1ec6a60524af91f9d01505ca0a83ef12cf5760e7761d4b4c9a8", + "review/layout-preview-astra-v4/composed-preview.svg": "6ed89a3482798afbb4c1853584d4e2412d7efded7be05d5523f24835e6087826", + "review/layout-preview-astra-v4/curtain-comparison.png": "e7ceedf10c580ffc2779e5d752b74a306bb507931abead6d1d7fed5d9842b4a9", + "review/layout-preview-astra-v4/curtain-junction-detail.png": "7eb2eac11828069aee08df7a961fa7bf600256d931d199a6ff5055f076ec1d15", + "review/layout-preview-astra-v4/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130", + "review/layout-preview-astra-v4/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c", + "review/layout-preview-astra-v4/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264", + "review/layout-preview-astra-v4/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260", + "review/layout-preview-astra-v4/fonts.conf": "3fecf336cb50d58261a01704b80ca2ebb0c26c9f5546ae7ef42c5a5139b1df1b", + "review/layout-preview-astra-v4/foreground-curtain.png": "9a2d9420dadf534d1e989997ae733a425e226a108ca63d6bad593e0de37f506d", + "review/layout-preview-astra-v4/foreground-curtain.svg": "c19142d4c8b2f5faed75a9d3260a2755f7609b01b190c48ad91672447e07c6ae", + "review/layout-preview-astra-v4/preview-validation.json": "9c154dd52ea9e5900c490415eadbe0d5296c1f5ae1531fd91cc4bc56cd15b1c8", + "review/layout-preview-astra-v4/reference-ink.png": "ad1d595d08725d0ea31e09d25b6b8084581fb10a9baca170fcb2b4b9c6c8579f", + "review/layout-preview-astra-v4/reference-ink.svg": "362bd19e588158d2e67ac3d9adb6ed5ff2b4e7cd64e9809c1d52af6d1bdaa41a", + "review/layout-preview-astra-v4/text-preview.png": "f2bde9899bf80b2a904dfad69cacca55070110db348b8664e428bf52152b8c45", + "review/layout-preview-astra-v4/text-preview.svg": "51f3a5f2bfed5f8ee6fdede5818a8f0d7afa954773ade715d38252bf37a746e0", + "review/layout-preview-astra-v4/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa", + "review/layout-preview-astra-v4/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee", + "review/layout-preview-astra-v4/v3-v4-comparison.png": "165f5f255c83a5675958ef42731d3d07257e7a410efd45487dc27fad4af4fd40", + "review/layout-preview-astra-v4/v3-v4-junction-comparison.png": "68c6733de075d7b8af7f32fc70c63b705c3f9aff7db95925f3962758c65c70b1", + "review/layout-preview-astra-v4/verse-0-ink.png": "52691dbf58024400d4c9b833b5462fead3e591bfe42f5d6b4724899948837989", + "review/layout-preview-astra-v4/verse-0-ink.svg": "5f765f4a3737357d1dc729765c605156d25db7b2e61db41662a3973e6114c850", + "review/layout-preview-astra-v4/verse-1-ink.png": "8e2ea55c3dc5abede57b7e0b08c6687ff092d65763676ff28561fe9070c5ce2d", + "review/layout-preview-astra-v4/verse-1-ink.svg": "35fbf3657b3e2843bd590b66d8465eb6d2668f48603c28fcabb035e293cf1d33", + "review/layout-preview-astra-v4/verse-2-ink.png": "ea5aacebd89f9c6dd9fe75da842a362a64b2dbdaba3d833e86458fd81ae34f4a", + "review/layout-preview-astra-v4/verse-2-ink.svg": "0db7333d35aeb8c428f08b126dd8e0f8b7c4f963f39a2176dc6015081d4f3782", + "review/layout-preview-astra-v4/without-curtain-1000.png": "0d9792bcd074971f9c6e8e9d43ba62e0dc949dc91cae67b267439745fb321591", + "review/layout-preview-astra-v4/without-curtain-2000.png": "d1411a9b2edee91c8dcaeda10caf39bc45a245d478fa864b30f8a803d857cc09", + "review/layout-preview-astra-v4/without-curtain-500.png": "59502b9428e4f67a23c2fbc67229506d1bacb42864427a0d31a0dcf9ee40d383", + "review/layout-preview-astra-v4/without-curtain.svg": "494c942badd2b07f82fd68b33583982cb9a37b64d14c483d551c075f0900811b", + "review/layout-preview-astra-v5/README.md": "834ae192edcefb960d2ce1a955237faca4460ee39480131317d6911f15544858", + "review/layout-preview-astra-v5/border-backing-preview.png": "81d1982f18068c2d37d6b526ba91e2752729f2fea2254a787167bd88987c5616", + "review/layout-preview-astra-v5/border-backing-preview.svg": "9bdc25e80a6f3a4e0a38f0e18210f47bbc33daee90c66614b7339ce94f42dcc9", + "review/layout-preview-astra-v5/build-preview.py": "1e3127478ece35df94f04192323482d30a94290ed00028016645c6d3fea756a5", + "review/layout-preview-astra-v5/cloud-interaction-detail.png": "1b0f7e17cd650dffb2680084de454f46106be8fd522884019b623ba25949ec35", + "review/layout-preview-astra-v5/composed-preview-1000.png": "51cf1312f7613a8fe32952204e28eb0d41a73cc20c134d83d5b138e03c9914e3", + "review/layout-preview-astra-v5/composed-preview-2000.png": "e81cedd0d1df161a40d65f916724c0c3e852f990618159b2888f4731d3b3e4ef", + "review/layout-preview-astra-v5/composed-preview-500.png": "057b7ae9140005e88619711a026987ef7b48f865b73c550b083172666ea965d9", + "review/layout-preview-astra-v5/composed-preview.svg": "31ede5888735f11024d362fb79b9d622b3dec3fec470054d8e595ee407217298", + "review/layout-preview-astra-v5/curtain-comparison.png": "4fd017872d0f4cd03cccc2ddb1a66c9f1815c3454a581c33a9bf9cc9d7a22581", + "review/layout-preview-astra-v5/curtain-junction-detail.png": "29d02965d7d3fe78e0558c8746c1c509bd1e078ba0febe38dcd38915147da97a", + "review/layout-preview-astra-v5/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130", + "review/layout-preview-astra-v5/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c", + "review/layout-preview-astra-v5/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264", + "review/layout-preview-astra-v5/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260", + "review/layout-preview-astra-v5/fonts.conf": "a405d10a74b35906d3fd8f997eef6c9f527b9630bf88b5c9594a9bf862ff1aba", + "review/layout-preview-astra-v5/foreground-curtain.png": "a69131c4ea105e610faff03a57ad7804f2eac420c03ac36c5cf4609d419bfd03", + "review/layout-preview-astra-v5/foreground-curtain.svg": "36d5b8255928e61b714e0821942ab710240969fa2c6cf306eac6545ed79dcc46", + "review/layout-preview-astra-v5/preview-validation.json": "9819289bf141bc108f0324717e0734168ae5104a77046567e4eada61d80f5666", + "review/layout-preview-astra-v5/reference-ink.png": "56513923c9aa8b2ca2617379999a43561c6b05a1aa4f42914ec0487d544fa743", + "review/layout-preview-astra-v5/reference-ink.svg": "6c2059d51c08679342ccde8db143c4a8cd68f4122b7b5a3781b4f0fac1490a38", + "review/layout-preview-astra-v5/text-preview.png": "ad5b99d82439d547dcc7d06a74e9431ddac40050ae1e820e9858eb4a2314e2e8", + "review/layout-preview-astra-v5/text-preview.svg": "ddfadbe9dc5f0e671a6856669315f245e8ba23f992347bad90f44d580d27c91c", + "review/layout-preview-astra-v5/title-ink.png": "cb0d3620a82d28fd50970e6bbba5be4318db418b2578c61c9209fcb655f060aa", + "review/layout-preview-astra-v5/title-ink.svg": "27d03213b219942a2800cba962d320c99b3ae9c8e91523587ff4ce95b45beaee", + "review/layout-preview-astra-v5/v4-v5-comparison.png": "e48166e93a2d23eb10f81e1cb87cab4c5766cfdbc09cf19050e61adb92ac59a1", + "review/layout-preview-astra-v5/v4-v5-junction-comparison.png": "d576298f859992ff6d1c38da14cbe653000313dacc28110aa7c36d0933462138", + "review/layout-preview-astra-v5/verse-0-ink.png": "ca240a715a006a87f40deef69a6de0c6e27e2f9a662b7971b5a8ee944062fba2", + "review/layout-preview-astra-v5/verse-0-ink.svg": "f00584994b39ebec0a04aedbe456aa9cd0ea283e0924ca280f9995bb803f4956", + "review/layout-preview-astra-v5/verse-1-ink.png": "ac92560704c2fdfdfe3194b3f390b5a91c68cc4db67e5396a9acf1286881eacd", + "review/layout-preview-astra-v5/verse-1-ink.svg": "191433d0459bced61d9d8e08345b76614e2b6ea4544bed1b19cc7a2257746daa", + "review/layout-preview-astra-v5/verse-2-ink.png": "b86f7608a18900abd66b95c4019969ce851439fd6699d9f579676d731b7e3a26", + "review/layout-preview-astra-v5/verse-2-ink.svg": "2cca7b142219050f36f8c3e3250b64e9bc30edfa7afb62c5eff2cd135e64457a", + "review/layout-preview-astra-v5/without-curtain-1000.png": "4db949f4d5a1ea8b56aa9dca27e2d1e038b0b175d3342d6ae88c70bb0d3f1620", + "review/layout-preview-astra-v5/without-curtain-2000.png": "a063b89297b6f6e1e4cf013b716125b97086e25d4e6a6d609be275f57faeed50", + "review/layout-preview-astra-v5/without-curtain-500.png": "0a2351848de37ba4b90a9e75fcc17d49bfc9a60e18d03a0b001c704cf9e7e4a2", + "review/layout-preview-astra-v5/without-curtain.svg": "1d890b8c2a75c2ce81572e397b3c1496ee802394f96810c652e7bbf7f39c554c", + "review/layout-preview-v1/README.md": "dd84b5f63ffedb84d0cad5b242a9d945b550e81df82bb4a71675cfbecf0495c1", + "review/layout-preview-v1/border-backing-preview.png": "5c335ab3425888f729f19608faf5909561a7c2087d1166a29b7fb6236aa7a9b1", + "review/layout-preview-v1/border-backing-preview.svg": "931e130a798a33be35a38752fcfea0f8c899e25bcafb9a40251497dc3c39fe30", + "review/layout-preview-v1/build-preview.py": "02c8b7b48caf63a6e6b06d0c67e5b0441c583beeda2d85ad30c349a729f86f5e", + "review/layout-preview-v1/composed-preview-1000.png": "30ac9768afe4b5839e894f478804da9957c5a3ab48c9263a4428da4edbd7a730", + "review/layout-preview-v1/composed-preview-2000.png": "0304b7d3d2bca3046b45d391d1093d150a4196bcf7d31552b0315f18621fa2fd", + "review/layout-preview-v1/composed-preview-500.png": "0e04b5d39270302c710c6d1ccdf99d51f2008c8c29f8b5ec29abb92e64e38907", + "review/layout-preview-v1/composed-preview.svg": "4797dd28f1330e5255ed53d9338ad4614d1a28bd1f5cc5268660482f95856d3f", + "review/layout-preview-v1/curtain-comparison.png": "870ff517d3f7a03f6ac29535add0bd814d019238c495d174b2edfa314bf8e47b", + "review/layout-preview-v1/curtain-junction-detail.png": "e699ad7f48542c076f9ba4636c38ad7df47582887835b1a1fa13f5277f87d322", + "review/layout-preview-v1/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130", + "review/layout-preview-v1/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c", + "review/layout-preview-v1/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264", + "review/layout-preview-v1/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260", + "review/layout-preview-v1/fonts.conf": "d260a1075d0536ca7e42e6f0d4701e0015b13e2f49a7da256856fd4f07d06cf0", + "review/layout-preview-v1/foreground-curtain.png": "92be2d148c4f8581f48c659c7981880b9b4a1b27cadd5003d466d20bf8581f88", + "review/layout-preview-v1/foreground-curtain.svg": "aaac72b44c4423dae42308af6076017cc491d49dd8560f409ca944a9054b2f29", + "review/layout-preview-v1/preview-validation.json": "91b6200b2346805da9b2c797ed61f07975cafbfbf9e84f677746bf2bd3a4accf", + "review/layout-preview-v1/reference-ink.png": "b65779df016759845852ec705e9325b512c274037f2d4f44b71f4a86cd91248b", + "review/layout-preview-v1/reference-ink.svg": "fa5673866345f955c93df273de286946ad91def05f10c313a828a5b5caa4beec", + "review/layout-preview-v1/text-preview.png": "2e02ee337794e76f8414195f541ab126d825d4f2e84347c92f642e3913ab8f67", + "review/layout-preview-v1/text-preview.svg": "b027a50379222be7ad002b4a806f490ad35cbcf7639e079468badab3714c8081", + "review/layout-preview-v1/title-ink.png": "73b8c268f1604d956c56863e02684d66934d011fe3dab28f14e756a40f8ae024", + "review/layout-preview-v1/title-ink.svg": "b2ca810f03405ae03b05793418b65ea0df3599d6ff2a8395b7036ff806da8b9c", + "review/layout-preview-v1/verse-0-ink.png": "7e9061d4663a5495e54170987a76bd85bdf957ea1a8500e1afe0537bd56f7e80", + "review/layout-preview-v1/verse-0-ink.svg": "5a3d5b6d06f919099fe47e93930731f4a261b46025672aa77d595b6352a75f87", + "review/layout-preview-v1/verse-1-ink.png": "83271ea2d9f2e068dc419b80d6f58ceeda55acd06392abad9b74f5a014d0d747", + "review/layout-preview-v1/verse-1-ink.svg": "e5af9ff770b72c85da69e098da1fa4967157cec9bc5db13109bcdc1ef2be74bb", + "review/layout-preview-v1/verse-2-ink.png": "d22f1e3b1d67ade6d4021e73a314b675d3a6d1e92548fcf591cdd777b88a807c", + "review/layout-preview-v1/verse-2-ink.svg": "9027bc23acf2edc1ed2b5bce110324d396b94bda6d0fb5c332f298154b7988b9", + "review/layout-preview-v1/verse-3-ink.png": "9eaecd5c3d3eae12ac7c76bc691ec192f8645b5a7ac40aa74c8a7c7a96d8f4e4", + "review/layout-preview-v1/verse-3-ink.svg": "5f200c654ba47fd8fb54b4d6ffc7968de5bad6ef83ddf7b2a7cfd3c4a8b8ebb2", + "review/layout-preview-v1/without-curtain-1000.png": "17eedd7ffe471d185cb6c1dd6b090bfd1f06b4517714529bffad4c749f547619", + "review/layout-preview-v1/without-curtain-2000.png": "3b136d72dca4aa18ed4c952d59d0a87a71f9e8120958e3847474112676925bb6", + "review/layout-preview-v1/without-curtain-500.png": "2e057afca6bf3b14a745bd3328f1833ad402f37cb948ce5b671c83101fdc1183", + "review/layout-preview-v1/without-curtain.svg": "5ef5b40c113193cedd1eba63e0b5c70c60dd823a65159137940a533604344260", + "review/layout-preview-v2/README.md": "7029b973c35219c108ae191bbfd43bf19bd4c79b6b24849bb062c77a85687ff0", + "review/layout-preview-v2/backing-comparison.png": "54336c48917204033be863f5a97b292d43c402b694ca450e7621afd86a827756", + "review/layout-preview-v2/border-backing-preview.png": "f368fa3c4e3bb68ebfa138c5d19824c33bf94fb3365f6e6b31ac50f277a1bb2e", + "review/layout-preview-v2/border-backing-preview.svg": "a1695255614e204ce8c0180ae3646c4a8f449cc54d24dac817c3f285336077b0", + "review/layout-preview-v2/build-preview.py": "b380476cb1872db5517bbacf0f44d9881e181340870aff99d8a5ab75d4c1e0fd", + "review/layout-preview-v2/composed-preview-1000.png": "7ff672992bb597ce6f63d071c32fe755d9d9d47f9651fd10ff6aa83198e78563", + "review/layout-preview-v2/composed-preview-2000.png": "29d74f488bc935fe7c1100bc0e8dc74cfef8f2f09ac9833cc7e5f6698aba4d49", + "review/layout-preview-v2/composed-preview-500.png": "9a33f271bb1c3e9084f983d0a340675422dbbad6fa05900fcfcccd3734c9d7a8", + "review/layout-preview-v2/composed-preview.svg": "42ddf9f7ee2761fc32e1800e23dc6f34e7f13a24ebd68c29f9911cea2e3ca490", + "review/layout-preview-v2/curtain-comparison.png": "5b97cdc928704fd2f0d16a1470b1bfa9dcfdc4feacf829e4eb336506348b5d1a", + "review/layout-preview-v2/curtain-junction-detail.png": "57f4a2786f90d3bd5446ca12151bb55681635dc59454059b1ecb20bd0b5931d3", + "review/layout-preview-v2/font-cache/037d20e60be746631d2b233d68ab4a1a-le64.cache-9": "464a2950361202013d83f869f3513422735df1d8e76f05a8ae4c2b0697091130", + "review/layout-preview-v2/font-cache/0e14d11dfa32bcd8ea4cd43b2e352131-le64.cache-9": "0a387174a7a8c5be94a53f86e5789c77da44d9700bad89aba12d090c8a64697c", + "review/layout-preview-v2/font-cache/a46a90b3114f4679f37745d4a8f3e608-le64.cache-9": "dec2022698647dc7bfd7f5354bc086c407992f4bc4ba0081d88ef03b55572264", + "review/layout-preview-v2/font-cache/f53da1bd49ea03cb387bf10765c2bba8-le64.cache-9": "f7c8c19ff02b7c463e758031944bc3777bc86aa8ed2cd961e9b27d4ec295a260", + "review/layout-preview-v2/fonts.conf": "8737a504b7dbe041a6b3d5cf96ae3039d40b6bcbe0842c7fbc463738a13b061e", + "review/layout-preview-v2/foreground-curtain.png": "92be2d148c4f8581f48c659c7981880b9b4a1b27cadd5003d466d20bf8581f88", + "review/layout-preview-v2/foreground-curtain.svg": "aaac72b44c4423dae42308af6076017cc491d49dd8560f409ca944a9054b2f29", + "review/layout-preview-v2/preview-validation.json": "0176dfe18f5e61e201be699d22a77da9494d98ad137df4aaedac9bbb556f59cb", + "review/layout-preview-v2/reference-ink.png": "91cba7a3b3139efdaeecb01cc40abb34dc02035e015879a0df38bb62ccd4747c", + "review/layout-preview-v2/reference-ink.svg": "2b57ed78a89309e2d4a0281476d21b1b2c0ac99638075581f6873ce5a8022f5e", + "review/layout-preview-v2/text-preview.png": "d2559475d6d908fb8844132e2db1ababa84cc5d64d7eea16b89ad205507333ec", + "review/layout-preview-v2/text-preview.svg": "325e020f8ff1d529a5ef48c45bb396ec38d91a3aaace6de8e6202b10873cf27e", + "review/layout-preview-v2/title-ink.png": "73b8c268f1604d956c56863e02684d66934d011fe3dab28f14e756a40f8ae024", + "review/layout-preview-v2/title-ink.svg": "b2ca810f03405ae03b05793418b65ea0df3599d6ff2a8395b7036ff806da8b9c", + "review/layout-preview-v2/verse-0-ink.png": "3c772feed78b5cfc4b2669c6cf248ac4d4002b7b8ae3ab8d06ad39d3a51574a2", + "review/layout-preview-v2/verse-0-ink.svg": "39f20cf6bd9c4b2222b3c47c57c20a4f1a352498687878540947c6e30b6a3669", + "review/layout-preview-v2/verse-1-ink.png": "d4fa2d04df13023c346ddca5182edc4c0fe0901b8eb352fecdf50e0cbe7c3aff", + "review/layout-preview-v2/verse-1-ink.svg": "7be7f61dfab438fff3b1dae28fa00cebdbb27568cb9477af0dec99839f2a9232", + "review/layout-preview-v2/verse-2-ink.png": "2c35c89bcc4d4715ad813e0cedcc63dfef02325c2831d7d9f25dd4d6eb4f7f55", + "review/layout-preview-v2/verse-2-ink.svg": "03624c77c33bd6ef166cf3a3f8308a38f65d6440e3f46990087ac96b948a7d41", + "review/layout-preview-v2/verse-3-ink.png": "9eaecd5c3d3eae12ac7c76bc691ec192f8645b5a7ac40aa74c8a7c7a96d8f4e4", + "review/layout-preview-v2/verse-3-ink.svg": "5f200c654ba47fd8fb54b4d6ffc7968de5bad6ef83ddf7b2a7cfd3c4a8b8ebb2", + "review/layout-preview-v2/without-curtain-1000.png": "77140f7f393fbb2ba626e968fd661976f725fdda25ba56a0a84f31853451513a", + "review/layout-preview-v2/without-curtain-2000.png": "7832b597183f9925fe755fc21b7783ee3ab810b99c4dda73291fd7b388b31b97", + "review/layout-preview-v2/without-curtain-500.png": "cc396a823b18cfb80c9f523120ff822ca74ad7d6ef6b103d64f5fb4ad1a5b076", + "review/layout-preview-v2/without-curtain.svg": "ffff56dec49826e92a5bae026802afb77fd39e001ae0fd7a8c3c455eebe968a8", + "source/art-fit.json": "f06f3666c0add4fd52084a0998d9e48146ec84e0f04e51dddcfc5ca2aa396388", + "source/art-master.png": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85", + "source/art-native.png": "ab234b91b260dba3b1a6973d765fc987e59339c21ec790d0ce51528e8fd98157", + "source/edit-prompt.txt": "0bd8e3c5c40d59dd6f1f9caf9094371d0c3f52748e48a9cbd924609da1205e5a", + "source/edit-reference.jpg": "0f6754052e7e739cf00a8f8360cb922112b2aa8501b8adfe981d9544e314108f", + "source/fit-art.py": "f9aea5aa6b192591db9db7c8d26c6d0a1ee272ab977ed3940891ca32c17acd07", + "source/generation.json": "bf4cd2254dc45d51c79a5d29abe40613a9a8530fbf17a02d60448cf64a5a0e36", + "source/refinement.md": "ea3a4f27292be136543286d4d67d7780ceeb4fb986992f428736de749de6ff4d" +} diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/refinement.md b/in-progress/cards/BP-001-moses/history/revisions/v04/source/refinement.md new file mode 100644 index 0000000..0b47bdd --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/refinement.md @@ -0,0 +1,5 @@ +# Moses v04 — continuous cloud/presence + +User feedback: the golden cloud reads as bubbles or orbs, weakening the intended Presence. This is a base-illustration correction, not a finish-mask adjustment. Preserve all non-cloud subjects and structure as closely as possible. Replace circular/scalloped cloud cells with continuous rising irregular pearl/ivory vapor layers, cool shadows and restrained warm rim light. + +Astra medium direction, built-in targeted image edit. The starting source is v03 `source/art-master.png`. Native-path reads failed sandbox initialization; the edit therefore uses the single conversation-visible 1000×1400 JPEG quality 93 derived from that master, retained as `edit-reference.jpg`. v03 remains preserved, and its pre-edit hashes are retained for a non-mutation check. No card assembly is authorized. diff --git a/in-progress/cards/BP-001-moses/history/revisions/v04/source/validate-art.py b/in-progress/cards/BP-001-moses/history/revisions/v04/source/validate-art.py new file mode 100644 index 0000000..8a8c9bf --- /dev/null +++ b/in-progress/cards/BP-001-moses/history/revisions/v04/source/validate-art.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""Validate retained art inputs, fit, review dimensions and v03 preservation only.""" +from pathlib import Path +from PIL import Image +import hashlib,json,subprocess,sys +p=Path(__file__).resolve().parent.parent +sha=lambda f:hashlib.sha256(f.read_bytes()).hexdigest() +prior=json.loads((p/'source/prior-revision-hashes.json').read_text()) +assert all(sha(p.parent/'v03'/f)==h for f,h in prior.items()), 'v03 changed' +fit=json.loads((p/'source/art-fit.json').read_text()) +assert sha(p/'source/art-native.png')==fit['sourceSHA256'] +assert sha(p/'source/art-master.png')==fit['outputSHA256'] +expected={'source/art-master.png':[2000,2800],'review/art-review.png':[1000,1400],'review/art-card-size.png':[500,700],'review/art-thumbnail.png':[250,350]} +before={f:sha(p/f) for f in expected} +for f,size in expected.items(): + with Image.open(p/f) as im: + assert list(im.size)==size + assert im.mode=='RGB' +subprocess.run([sys.executable,str(p/'source/fit-art.py')],check=True) +assert before=={f:sha(p/f) for f in expected}, 'fit not byte-repeatable' +result={'status':'pass','scope':'Art source/fit/review only; no card assembly or material validation','priorRevision':'v03','priorFilesUnchanged':len(prior),'nativeSourceHashVerified':True,'masterHashVerified':True,'reviewDimensions':expected,'repeatableFitAndReviews':True,'nonCloudPixelIdentity':'Not claimed; visually reviewed generative edit'} +(p/'review/source-validation.json').write_text(json.dumps(result,indent=2)+'\n') +print(json.dumps(result,indent=2)) diff --git a/in-progress/index.json b/in-progress/index.json index 551613f..db9868d 100644 --- a/in-progress/index.json +++ b/in-progress/index.json @@ -28,6 +28,15 @@ "stage": "approved", "path": "artifacts/cards/BE-042-calling-of-the-first-disciples" }, + { + "cardId": "BP-001", + "title": "Moses", + "folderName": "BP-001-moses", + "rarity": "Legendary", + "selectedRevision": "v05", + "stage": "approved", + "path": "artifacts/cards/BP-001-moses" + }, { "cardId": "BP-002", "title": "David",