fix: per-item transform in batch export (correct placement across sizes)
QueuePanel reused one pixel TransformState for all non-fine-tuned items, mis-placing garments/prints of other sizes. Store the ratio-based template instead and recompute each item from its own garment/print size. - add core.composer.image_size() and resolve_transform() (pure, tested) - queue: set_template() replaces set_transform(); per-item resolve + size cache - template_panel: expose current_template(); main_window pushes it to the queue - tests: +4 covering fine-tune precedence, per-size recompute, empty inputs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+42
-2
@@ -9,8 +9,8 @@ from PIL import Image
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
from core.composer import compose
|
||||
from core.models import ExportOptions, TransformState
|
||||
from core.composer import compose, resolve_transform
|
||||
from core.models import ExportOptions, Template, TransformState
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# helpers
|
||||
@@ -207,5 +207,45 @@ class TestJpgOutput(_TmpDir, unittest.TestCase):
|
||||
self.assertTrue(result.success, result.error)
|
||||
|
||||
|
||||
class TestResolveTransform(unittest.TestCase):
|
||||
"""resolve_transform: per-item placement for batch export."""
|
||||
|
||||
def _square_template(self):
|
||||
# 25% box centred at (0.40,0.40)-(0.65,0.65); square print fills the box.
|
||||
return Template(name="sq", x_ratio=0.40, y_ratio=0.40,
|
||||
width_ratio=0.25, height_ratio=0.25)
|
||||
|
||||
def test_fine_tuned_transform_wins(self):
|
||||
own = TransformState(x=1, y=2, width=3, height=4)
|
||||
out = resolve_transform(self._square_template(), own, (1000, 1000), (50, 50))
|
||||
self.assertIs(out, own)
|
||||
|
||||
def test_differently_sized_garments_get_different_pixels(self):
|
||||
"""The core bug: one shared pixel transform mis-places other sizes.
|
||||
|
||||
Each garment must be laid out from its own dimensions."""
|
||||
tpl = self._square_template()
|
||||
small = resolve_transform(tpl, None, (1000, 1000), (100, 100))
|
||||
large = resolve_transform(tpl, None, (2000, 3000), (100, 100))
|
||||
|
||||
# 1000px garment: 25% box = 250px, square print fills it, centred at 0.525.
|
||||
self.assertAlmostEqual(small.width, 250.0)
|
||||
self.assertAlmostEqual(small.x, 400.0)
|
||||
# 2000x3000 garment: box is 500x750, square print contain-fits to 500,
|
||||
# and x stays proportional — proving per-item recomputation.
|
||||
self.assertAlmostEqual(large.width, 500.0)
|
||||
self.assertAlmostEqual(large.x, 800.0)
|
||||
self.assertNotAlmostEqual(small.x, large.x)
|
||||
|
||||
def test_none_template_returns_none(self):
|
||||
self.assertIsNone(resolve_transform(None, None, (100, 100), (10, 10)))
|
||||
|
||||
def test_missing_sizes_return_none(self):
|
||||
tpl = self._square_template()
|
||||
self.assertIsNone(resolve_transform(tpl, None, None, (10, 10)))
|
||||
self.assertIsNone(resolve_transform(tpl, None, (100, 100), None))
|
||||
self.assertIsNone(resolve_transform(tpl, None, (0, 0), (10, 10)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user