Import S-CANVAS source + iter=1~7 lint cleanup
S-CANVAS (Saman Corp.) — DXF + DEM + AI 기반 3D 조감도 생성 엔진. ~24k LOC Python (scanvas_maker.py 7072 LOC GUI + 구조물 파서/빌더 다수). 이 커밋은 7-iter cleanup이 적용된 상태로 import: - F821 8 + B023 6: 비동기 lambda + except/loop 변수 캡처 NameError (Py3.13에서 reproduce 확인된 진짜 버그) - RUF012 4 + RUF013 1: ClassVar / implicit Optional 명시화 - F811/B905/B904/F401/F841/W293/F541/UP/SIM/RUF/PLR 700+ cleanup/modernization 신규 파일: - ruff.toml: target=py313, Korean unicode/저자 스타일/도메인 복잡도 무력화 - requirements-py313.txt: pyproj>=3.7, scipy>=1.14, numpy>=2.0.2 (Py3.13 wheel) - .gitignore: gcp-key.json, 캐시, 백업, 생성 이미지 제외 검증: ruff 0 errors, py_compile 0 errors, import 33/33 OK on Py3.13.13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
_build_icon.py
Normal file
67
_build_icon.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Pre-generate scanvas_S.ico from Design/logo_V2.png for PyInstaller spec.
|
||||
|
||||
Called by build.bat before PyInstaller runs. Standalone so encoding issues in
|
||||
.bat don't break it. Mirrors SCanvasApp._setup_window_icon logic.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy import ndimage as nd
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = Path(__file__).resolve().parent
|
||||
src = root / "Design" / "logo_V2.png"
|
||||
dst_dir = root / "cache" / "icons"
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
ico = dst_dir / "scanvas_S.ico"
|
||||
|
||||
if not src.exists():
|
||||
print(f"[icon] source not found: {src}")
|
||||
return 1
|
||||
|
||||
pil = Image.open(src).convert("RGBA")
|
||||
arr = np.asarray(pil).copy()
|
||||
v = np.maximum.reduce([arr[..., 0], arr[..., 1], arr[..., 2]])
|
||||
arr[..., 3] = np.where(v < 90, 0, arr[..., 3])
|
||||
|
||||
# Crop left ~32% to isolate the 'S' letter
|
||||
crop_w = min(arr.shape[1], 850)
|
||||
carr = arr[:, :crop_w, :]
|
||||
mask = carr[..., 3] > 100
|
||||
labeled, ncomp = nd.label(mask)
|
||||
if ncomp == 0:
|
||||
print("[icon] no foreground content found")
|
||||
return 1
|
||||
sizes = nd.sum(mask, labeled, range(1, ncomp + 1))
|
||||
max_size = float(sizes.max())
|
||||
keep = [i + 1 for i, s in enumerate(sizes) if s >= max_size * 0.1]
|
||||
keep_mask = np.isin(labeled, keep)
|
||||
cc = carr.copy()
|
||||
cc[..., 3] = np.where(keep_mask, carr[..., 3], 0)
|
||||
|
||||
ys, xs = np.where(keep_mask)
|
||||
cropped = Image.fromarray(
|
||||
cc[int(ys.min()):int(ys.max()) + 1, int(xs.min()):int(xs.max()) + 1, :],
|
||||
"RGBA",
|
||||
)
|
||||
cw, ch = cropped.size
|
||||
side = max(cw, ch)
|
||||
pad = max(int(side * 0.04), 4)
|
||||
sp = side + 2 * pad
|
||||
sq = Image.new("RGBA", (sp, sp), (0, 0, 0, 0))
|
||||
sq.paste(cropped, ((sp - cw) // 2, (sp - ch) // 2), cropped)
|
||||
sq.save(
|
||||
ico, format="ICO",
|
||||
sizes=[(16, 16), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
|
||||
)
|
||||
print(f"[icon] {ico} ({ico.stat().st_size} bytes)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user