Skip to content

feat(MIT): mask-aware narrow-column wrap + safe-area centering (distance-transform interior + pole-of-inaccessibility) #179

Closed

Description

@xenodeve

Parent

PRD #178 (Render Parity). This is the root-cause slice for "ours reads as a wide paragraph, theirs reads as manga".

What to build

Today bubble-fit wraps English to the balloon's bounding-box width (mw = w_box × 0.92), so a wide round balloon produces wide lines regardless of the original (tall-narrow) Japanese footprint. MangaTranslator instead wraps to a measured safe interior of the actual mask shape and centers text on a safe anchor (text_renderer.py:174-181, image_utils.py:173-348 — see docs/research/translator-deep-dissection.md).

Add a pure safe_area module: given a cleaned balloon mask, compute the distance-transform safe-area box (width/height) + centroid anchor, with a pole-of-inaccessibility fallback (use the max-distance pixel when dist(centroid) < 0.70 × max_dist) so text isn't centered in a conjoined-bubble neck. Thread the balloon mask into _bubble_fit_font_size so the wrap width is the measured interior width (not the bbox), and center the rendered block on the anchor (fix the current left-aligned aspect padding).

Opt-in behind the existing render.bubble_area_fit flag; off → byte-identical.

Acceptance criteria

  • Pure safe_area(mask) -> (safe_box, anchor) unit-tested with synthetic masks (round, conjoined-neck, irregular) — no PIL/ML imports; pole-of-inaccessibility fallback verified on a neck mask
  • Bubble-fit wraps to the measured interior width; tall-narrow source → narrower English column (verify on the One Punch-Man benchmark)
  • Rendered block centered on the safe anchor (aspect padding no longer left-aligned)
  • bubble_area_fit off → render byte-identical
  • PIPELINE.md §5 provenance updated; benchmark scorecard (MIT/BENCHMARK.md) updated

Blocked by

None - can start immediately


Parent

PRD #178 (Render Parity) — slice ราก ของปัญหา "ของเราอ่านเป็นย่อหน้า ของเขาอ่านเป็นมังงะ"

สิ่งที่ต้องสร้าง

ตอนนี้ bubble-fit wrap อังกฤษตาม ความกว้าง bounding box ของบับเบิล (mw = w_box × 0.92) บับเบิลกลมกว้างจึงได้บรรทัดกว้างไม่ว่าต้นฉบับญี่ปุ่นจะสูงแคบแค่ไหน MangaTranslator wrap ตาม safe interior ที่วัดจากรูปทรง mask จริง แล้วจัดกึ่งกลางที่ anchor ปลอดภัย (text_renderer.py:174-181, image_utils.py:173-348 — ดู docs/research/translator-deep-dissection.md)

เพิ่ม pure module safe_area: จาก mask บับเบิลที่ clean แล้ว คำนวณ safe-area box (กว้าง/สูง) + centroid anchor พร้อม pole-of-inaccessibility fallback (ใช้ pixel ที่ distance สูงสุดเมื่อ dist(centroid) < 0.70 × max_dist) เพื่อไม่ให้ข้อความไปอยู่กลางคอคอดของบับเบิลเชื่อม แล้ว thread mask เข้า _bubble_fit_font_size ให้ wrap ตามความกว้าง interior ที่วัดได้ (ไม่ใช่ bbox) และจัดบล็อกข้อความกึ่งกลางที่ anchor (แก้ aspect padding ที่ตอนนี้ชิดซ้าย)

opt-in หลัง flag render.bubble_area_fit เดิม ปิด → byte-identical

เกณฑ์การยอมรับ

  • safe_area(mask) -> (safe_box, anchor) (pure) unit-test ด้วย mask สังเคราะห์ (กลม/คอคอด/ไม่สม่ำเสมอ) ไม่ import PIL/ML; ยืนยัน pole-of-inaccessibility บน mask คอคอด
  • bubble-fit wrap ตามความกว้าง interior; ต้นฉบับสูงแคบ → คอลัมน์อังกฤษแคบลง (ยืนยันบน benchmark One Punch-Man)
  • บล็อกข้อความจัดกึ่งกลางที่ anchor (aspect padding ไม่ชิดซ้ายแล้ว)
  • bubble_area_fit ปิด → byte-identical
  • อัปเดต PIPELINE.md §5 + scorecard ใน MIT/BENCHMARK.md

ถูกบล็อกโดย

ไม่มี - เริ่มได้ทันที

Activity

  1. added
    effort:xhighEffort: xhigh (heavy/architectural/byte-identical)
    on Jun 11, 2026
  2. xenodeve commented on Jun 13, 2026

    @xenodeve
    CollaboratorAuthor

    ✅ Done — delivered (bubble-fit) + superseded (clean-layout). safe_area(mask) → (box, anchor) shipped in safe_area.py (distance-transform interior + pole-of-inaccessibility anchor), unit-tested in test/test_safe_area.py; bubble-fit wraps to the measured interior width. The narrow-column goal — English breaks where the source column did, not a wide paragraph — is now delivered more directly by the clean_layout rework: clean_wrap_width wraps to the source footprint width (#264), verified in-app (dialogue ~2 words/line + hyphenation, narration narrow tall). Closing as delivered.

    TH — เสร็จ: safe_area ใน safe_area.py (distance-transform interior + pole-of-inaccessibility), test ใน test_safe_area.py; เป้าหมาย narrow-column (อังกฤษตัดบรรทัดตามคอลัมน์ต้นฉบับ) ส่งมอบตรงกว่าโดย clean_layout clean_wrap_width (#264) ยืนยันในแอป ปิด issue

  3. added a commit that references this issue on Jun 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureMITeffort:xhighEffort: xhigh (heavy/architectural/byte-identical)model:opus-4.8Recommended: Opus 4.8 (1M)ready-for-agentReady for AI agent to implement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions