new grayscale output for testing laser, not working, will change to new design

This commit is contained in:
ykcheungab
2026-09-30 16:12:06 +08:00
parent 37e3e8400b
commit f0454253c2
27 changed files with 381132 additions and 270 deletions
+16 -3
View File
@@ -488,7 +488,9 @@ def cmd_texture_views(revision_dir: Path, args: argparse.Namespace) -> int:
overrides = texture_overrides_from_args(args)
try:
settings = texture_views.resolve_views_settings(paths, overrides)
pairs = texture_views.resolve_view_pairs(paths, settings)
pairs = texture_views.resolve_view_pairs(
paths, settings, grayscale=bool(getattr(args, "grayscale", False))
)
output_dir = resolve_texture_output_dir(root, paths, args.output_dir)
except TextureConfigError as exc:
for message in str(exc).splitlines():
@@ -496,7 +498,9 @@ def cmd_texture_views(revision_dir: Path, args: argparse.Namespace) -> int:
return 2
targets = texture_views.multiview_output_paths(output_dir, pairs)
job = texture_views.build_multiview_job(paths, settings, pairs, output_dir)
job = texture_views.build_multiview_job(
paths, settings, pairs, output_dir, grayscale=bool(getattr(args, "grayscale", False))
)
job_path = texture_views.write_multiview_job(job, output_dir)
# The job path is a fact about an intermediate file, not a deliverable: verbose only.
console.detail(f"Wrote multi-view texture job: {job_path}")
@@ -2096,7 +2100,16 @@ def main(argv: list[str] | None = None) -> None:
help="Edge sampling grid for partially covered patches",
)
texture_views_p.add_argument(
"--seed", type=int, default=None, help="Base seed; pair i uses seed + i"
"--seed", type=int, default=None, help="Base seed; every pair shares it deliberately"
)
texture_views_p.add_argument(
"--grayscale",
action="store_true",
help=(
"Also write a laser-engraving set per view (<stem>_gray.png/.svg/.pdf): pattern "
"dark on a white page, tone = Rec.709 luminance of the colour it replaces. "
"Additive - the colour PNG/SVG/PDF set is unchanged"
),
)
manifest_p = sub.add_parser("manifest", help="Write release manifest with file hashes")
+23 -3
View File
@@ -111,13 +111,33 @@ class TextureOutputTarget:
"""Backward-compatible convenience for the PNG artifact path."""
return Path(output_dir) / self.artifact_filename("png")
def artifact_paths(self, output_dir: Path) -> dict[str, Path]:
"""Return absolute artifact paths in stable PNG/SVG/PDF order."""
def gray_filename(self, format_name: str) -> str:
"""Return the laser-engraving companion's name for one format.
A separate stem (``<stem>_gray.<ext>``) rather than a replacement, so the colour
production set stays byte-identical and a print run cannot accidentally consume
engraving artwork.
"""
return f"{Path(self.filename).stem}_gray.{format_name}"
def artifact_paths(self, output_dir: Path, grayscale: bool = False) -> dict[str, Path]:
"""Return absolute artifact paths in stable PNG/SVG/PDF order.
``grayscale`` appends the laser-engraving set (``gray_png``/``gray_svg``/``gray_pdf``)
after the print set. Off by default, so the single-view path and every existing caller
are unaffected.
"""
resolved_dir = Path(output_dir).resolve()
return {
paths = {
format_name: resolved_dir / self.artifact_filename(format_name)
for format_name in TEXTURE_ARTIFACT_FORMATS
}
if grayscale:
for format_name in TEXTURE_ARTIFACT_FORMATS:
paths[f"gray_{format_name}"] = (
resolved_dir / self.gray_filename(format_name)
)
return paths
def to_job_entry(self, output_dir: Path) -> dict[str, Any]:
"""Return the v3 job entry Blender consumes (absolute paths, ordered)."""
+13 -3
View File
@@ -166,6 +166,8 @@ class ViewPair:
target: TextureOutputTarget
seed: int | None
page_mode: str = PAGE_DEFAULTS["mode"]
#: Also emit the laser-engraving PNG for this pair (`--grayscale`).
grayscale: bool = False
def to_job_entry(self, output_dir: Path) -> dict[str, Any]:
return {
@@ -177,7 +179,9 @@ class ViewPair:
"page": self.page_mode,
"artifacts": {
format_name: str(path)
for format_name, path in self.target.artifact_paths(output_dir).items()
for format_name, path in self.target.artifact_paths(
output_dir, grayscale=self.grayscale
).items()
},
}
@@ -478,6 +482,7 @@ def resolve_view_pairs(
paths: RevisionPaths,
settings: TexturePatchSettings,
block: Mapping[str, Any] | None = None,
grayscale: bool = False,
) -> tuple[ViewPair, ...]:
"""Resolve the views block into concrete pairs, or raise ViewPairConfigError."""
if block is None:
@@ -520,6 +525,7 @@ def resolve_view_pairs(
),
seed=None if seed is None else int(seed),
page_mode=str(raw.get("page") or default_page_mode),
grayscale=grayscale,
)
)
return tuple(pairs)
@@ -623,6 +629,7 @@ def build_multiview_job(
pairs: Sequence[ViewPair],
output_dir: Path,
block: Mapping[str, Any] | None = None,
grayscale: bool = False,
) -> dict[str, Any]:
"""Build the JSON job ``random_patch_multiview.py`` consumes."""
if not pairs:
@@ -633,6 +640,9 @@ def build_multiview_job(
return {
"schema": MULTIVIEW_JOB_SCHEMA,
"mode": "patches-multiview",
# Also emit the laser-engraving PNG per view. Recorded in the job so the run's
# inputs are fully described by the job rather than by how it was invoked.
"grayscale": bool(grayscale),
"revision_dir": str(paths.root.resolve()),
"source_blend": str(paths.source_blend.resolve()),
"objects": {"mesh": settings.mesh},
@@ -661,10 +671,10 @@ def multiview_output_paths(
output_dir: Path,
pairs: Sequence[ViewPair],
) -> list[tuple[ViewPair, dict[str, Path]]]:
"""Pair each view with its ordered PNG/SVG/PDF artifact paths."""
"""Pair each view with its ordered PNG/SVG/PDF (and optional laser-gray) artifact paths."""
resolved_dir = Path(output_dir).resolve()
return [
(pair, pair.target.artifact_paths(resolved_dir))
(pair, pair.target.artifact_paths(resolved_dir, grayscale=pair.grayscale))
for pair in pairs
]