fix(characters): remove divider lines from the concept sheet

The sheet composer drew light lines between the three poses and between
the top and bottom rows, cutting through capes and swords.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-10-02 07:49:19 +02:00
parent b50855ea2e
commit 6d1c6c1b88
3 changed files with 39 additions and 5 deletions

View File

@ -10,6 +10,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
- **Filter the page grid by status.** Each legend entry is now a toggle button with the number of pages in that status; choose one or several to show only pages that have any of them. It combines with *Show only pages that need me*, the summary says how many pages are shown, *Show all pages* clears everything, a status no page has is greyed out, and the buttons announce their on/off state and work from the keyboard.
### Fixed
- **No divider lines on the character concept sheet.** The Konzeptbild composer drew light lines between the three poses and between the top and bottom rows, which cut through capes and swords. They are gone. Sheets already stored have the lines baked into the image file and need to be regenerated to lose them.
- **Review opens at the top.** Opening a page in review no longer keeps the scroll position of the page grid, which could leave the sticky page bar covering the top of the page picture.

View File

@ -3617,7 +3617,6 @@ def _contain_image(raw: bytes, width: int, height: int, padding: int = 12) -> Im
def _compose_character_reference(images: dict[str, bytes], prop_count: int) -> str:
"""Assemble panels without cropping or asking the model to draw a layout."""
canvas = Image.new("RGB", (1536, 1536), (246, 242, 234))
draw = ImageDraw.Draw(canvas)
with Image.open(io.BytesIO(images["turnaround"])) as source:
turnaround = ImageOps.fit(source.convert("RGB"), (1536, 1024), Image.Resampling.LANCZOS)
# The model receives an explicit equal-thirds triptych. Crop only those
@ -3646,10 +3645,7 @@ def _compose_character_reference(images: dict[str, bytes], prop_count: int) -> s
panel = _contain_image(images[f"prop{index + 1}"], width, 512, 18)
x = scene_width + tile_width * index
canvas.paste(panel, (x, 1024))
draw.line((x, 1024, x, 1536), fill=(205, 198, 186), width=3)
draw.line((0, 1024, 1536, 1024), fill=(205, 198, 186), width=4)
draw.line((512, 0, 512, 1024), fill=(218, 212, 202), width=2)
draw.line((1024, 0, 1024, 1024), fill=(218, 212, 202), width=2)
# No divider lines between panels: they cut through capes and swords at the panel edges.
out = io.BytesIO()
canvas.save(out, format="PNG", optimize=True)
return "data:image/png;base64," + base64.b64encode(out.getvalue()).decode("ascii")

View File

@ -0,0 +1,37 @@
"""The character concept sheet is composed without divider lines between panels."""
from __future__ import annotations
import base64
import io
import os
import tempfile
import unittest
os.environ.setdefault("CONFIG_DIR", tempfile.mkdtemp(prefix="ttvc-refsheet-test-config-"))
from PIL import Image
import routes.conversation as conversation
BG = (246, 242, 234)
def _png(color):
out = io.BytesIO()
Image.new("RGB", (1536, 1024), color).save(out, format="PNG")
return out.getvalue()
class ComposeReferenceNoDividers(unittest.TestCase):
def test_no_lines_between_panels(self):
# Every panel is the same colour as the sheet background, so any pixel that differs is a drawn line.
images = {k: _png(BG) for k in ("turnaround", "scene", "prop1", "prop2")}
for props in (0, 1, 2):
uri = conversation._compose_character_reference(images, props)
img = Image.open(io.BytesIO(base64.b64decode(uri.split(",", 1)[1]))).convert("RGB")
self.assertEqual(img.size, (1536, 1536))
self.assertEqual(img.getcolors(maxcolors=16), [(1536 * 1536, BG)], f"divider drawn with {props} props")
if __name__ == "__main__":
unittest.main()