Rework layout engine around exact-glyph collision, add tests and docs sync
Replace the old bbox/heuristic placement (scale search rounds, large-font capping, stratified sampling, fill-retry ladders) with an area-model font sizing pass feeding a C++ exact-glyph collision engine (centroid-biased spiral + random probing, HD clearance refinement, density/hole optimization). Simplify the frontend advanced-params panel and JobParams type to match the surviving config surface, add a layout-constraints test suite and a repeatable benchmark tool, and bring docs/*.md back in sync with current code (plus new TESTING.md and DEPLOYMENT.md). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+18
-81
@@ -31,8 +31,6 @@ MODE = "IMAGE"
|
||||
# --- Image Mode ---
|
||||
MASK_IMAGE_PATH = "7887.png"
|
||||
IMAGE_CANVAS_MODE = "WIDTH"
|
||||
EXPAND_FOR_SPIRAL = True # 放大画布使螺旋填充覆盖边角
|
||||
EXPAND_RATIO = 2.5 # 更大倍率确保覆盖边缘
|
||||
FILL_CORNERS = False
|
||||
CORNER_FILL_RATIO = 0.15
|
||||
|
||||
@@ -42,11 +40,12 @@ MASK_FONT_PATH = str(PROJECT_DEFAULT_FONT)
|
||||
MASK_FONT_SIZE = 3000
|
||||
|
||||
# --- 自动画幅与清晰度 ---
|
||||
AUTO_EXPAND_CANVAS = True
|
||||
BASE_HD_WIDTH = 8000
|
||||
# 默认 4k 级画布:打印/激光足够清晰,比 8k 渲染快约 4×
|
||||
BASE_HD_WIDTH = 4000
|
||||
BASE_HD_HEIGHT = 4000
|
||||
MIN_READABLE_HEIGHT_PX = 25
|
||||
WORK_SCALE = 0.25
|
||||
MIN_READABLE_HEIGHT_PX = 22
|
||||
# 运算网格缩放:0.18 在速度/质量之间更均衡
|
||||
WORK_SCALE = 0.18
|
||||
|
||||
# --- 阴阳刻 ---
|
||||
FILL_ON = "BLACK"
|
||||
@@ -68,53 +67,17 @@ FONT_FALLBACK_PATHS = (
|
||||
|
||||
# --- 填充策略 ---
|
||||
N_REPETITIONS = 1
|
||||
TARGET_FILL_RATIO = 0.0 # 关闭填充率检测
|
||||
# 面积模型目标填充率:中文实心笔画像素占比约 0.35–0.55。
|
||||
# 略偏保守以保证 scale=1.0 首次就能放满,减少多轮重试。
|
||||
TARGET_FILL_RATIO = 0.45
|
||||
SIZE_RATIO = 2.0
|
||||
PACKING_EFFICIENCY = 0.85
|
||||
|
||||
# --- 分层采样(边缘覆盖) ---
|
||||
ENABLE_STRATIFIED_SAMPLING = True
|
||||
STRATIFIED_BANDS = 3 # Mix Center, Middle, and Edge
|
||||
|
||||
# --- 填充率补偿(低填充时略增字号) ---
|
||||
GROW_FONT_ON_LOW_FILL = False # 关闭
|
||||
GROW_FONT_STEP = 1.05
|
||||
|
||||
# --- 填充率检测 ---
|
||||
MIN_ACCEPT_FILL_RATIO = 0.75
|
||||
FILL_RETRY_RELAX_LARGE_CAP = True
|
||||
FILL_RETRY_MAX_ROUNDS = 3
|
||||
FILL_RETRY_MAX_SCALE = 1.5
|
||||
PACKING_EFFICIENCY = 0.9
|
||||
|
||||
# --- 智能字号搜索 ---
|
||||
REQUIRE_ALL_WORDS = True
|
||||
MIN_FONT_SIZE = int(MIN_READABLE_HEIGHT_PX * WORK_SCALE)
|
||||
USER_MIN_FONT_SIZE = None
|
||||
USER_MAX_FONT_SIZE = None
|
||||
MIN_FONT_FLOOR = 2
|
||||
FONT_SCALE_MIN = 0.5
|
||||
FONT_SCALE_MAX = 1.2
|
||||
SCALE_SEARCH_STEPS = 7
|
||||
SCALE_SEARCH_ROUNDS = 5
|
||||
SCALE_DECAY = 0.85
|
||||
SCALE_FLOOR = 0.25
|
||||
AUTO_SHRINK_ROUNDS = 4
|
||||
LOG_WEIGHT_RATIO = 0.72
|
||||
RANK_WEIGHT_RATIO = 0.28
|
||||
|
||||
# --- 大字号智能降级 ---
|
||||
# 开启后,如果填不满,会自动尝试减少大字号的数量,给小词腾空间
|
||||
ENABLE_SMART_LARGE_FONT_REDUCTION = True
|
||||
LIMIT_LARGE_FONTS = True
|
||||
LARGE_FONT_LIMIT_RATIO = 0.2 # 初始允许 20% 的词是大字
|
||||
LARGE_FONT_THRESHOLD_RATIO = 0.8 # 超过最大字号 80% 算大字
|
||||
LARGE_FONT_CAP_RATIO = 0.6 # 被限制时,缩小到阈值的 60%
|
||||
|
||||
# --- 点阵补偿 ---
|
||||
ENABLE_DOT_MATRIX = False
|
||||
DOT_SPACING = 15
|
||||
DOT_RADIUS = 0
|
||||
DOT_SAFETY_BUFFER = 12
|
||||
|
||||
# --- 画布重试 ---
|
||||
CANVAS_RETRY_MAX_ROUNDS = 1
|
||||
@@ -138,51 +101,36 @@ LIGHT_COLOR_PALETTE = (
|
||||
FONT_COLOR = "#000000" # 统一字体颜色,None 则使用调色板
|
||||
|
||||
# --- 输出 ---
|
||||
MAX_ATTEMPTS = 5
|
||||
OUTPUT_DIR = "."
|
||||
OUTPUT_PREFIX = ""
|
||||
OUTPUT_PNG = "Efficient_Result_HD_AutoResize.png"
|
||||
OUTPUT_SVG = "Efficient_Result_HD_AutoResize.svg"
|
||||
DB_PATH = "wordcloud_hd.db"
|
||||
METRICS_FILE = "metrics.json"
|
||||
SAVE_DEBUG_IMAGES = True
|
||||
SAVE_DEBUG_IMAGES = False
|
||||
DEBUG_OUTPUT_DIR = "output"
|
||||
|
||||
# --- 可复现性 ---
|
||||
SEED = None
|
||||
LAYOUT_ORDER_MODE_SORTED = "SORTED"
|
||||
LAYOUT_ORDER_MODE_INTERLEAVED_RANDOM = "INTERLEAVED_RANDOM"
|
||||
VALID_LAYOUT_ORDER_MODES = (
|
||||
LAYOUT_ORDER_MODE_SORTED,
|
||||
LAYOUT_ORDER_MODE_INTERLEAVED_RANDOM,
|
||||
)
|
||||
LAYOUT_ORDER_MODE = LAYOUT_ORDER_MODE_SORTED
|
||||
LAYOUT_SEED = None
|
||||
|
||||
KNOWN_CONFIG_KEYS = {
|
||||
'MODE', 'MASK_IMAGE_PATH', 'IMAGE_CANVAS_MODE', 'EXPAND_FOR_SPIRAL', 'EXPAND_RATIO', 'FILL_CORNERS',
|
||||
'CORNER_FILL_RATIO', 'MASK_TEXT', 'MASK_FONT_PATH', 'MASK_FONT_SIZE', 'AUTO_EXPAND_CANVAS',
|
||||
'MODE', 'MASK_IMAGE_PATH', 'IMAGE_CANVAS_MODE', 'FILL_CORNERS',
|
||||
'CORNER_FILL_RATIO', 'MASK_TEXT', 'MASK_FONT_PATH', 'MASK_FONT_SIZE',
|
||||
'BASE_HD_WIDTH', 'BASE_HD_HEIGHT', 'MIN_READABLE_HEIGHT_PX', 'WORK_SCALE', 'FILL_ON', 'EXCEL_PATH',
|
||||
'DATA_COL_INDEX', 'WEIGHT_COL_INDEX', 'WEIGHT_COL_NAME', 'REMOVE_DUPLICATES', 'ENABLE_STROKE_WEIGHTS',
|
||||
'WC_FONT_PATH',
|
||||
'FONT_FALLBACK_PATHS',
|
||||
'N_REPETITIONS', 'TARGET_FILL_RATIO', 'SIZE_RATIO', 'PACKING_EFFICIENCY', 'ENABLE_STRATIFIED_SAMPLING',
|
||||
'STRATIFIED_BANDS', 'GROW_FONT_ON_LOW_FILL', 'GROW_FONT_STEP', 'MIN_ACCEPT_FILL_RATIO',
|
||||
'FILL_RETRY_RELAX_LARGE_CAP', 'FILL_RETRY_MAX_ROUNDS', 'FILL_RETRY_MAX_SCALE', 'REQUIRE_ALL_WORDS',
|
||||
'MIN_FONT_SIZE', 'USER_MIN_FONT_SIZE', 'USER_MAX_FONT_SIZE', 'MIN_FONT_FLOOR', 'FONT_SCALE_MIN',
|
||||
'FONT_SCALE_MAX', 'SCALE_SEARCH_STEPS', 'SCALE_SEARCH_ROUNDS', 'SCALE_DECAY', 'SCALE_FLOOR',
|
||||
'LOG_WEIGHT_RATIO', 'RANK_WEIGHT_RATIO',
|
||||
'AUTO_SHRINK_ROUNDS', 'ENABLE_SMART_LARGE_FONT_REDUCTION', 'LIMIT_LARGE_FONTS',
|
||||
'LARGE_FONT_LIMIT_RATIO', 'LARGE_FONT_THRESHOLD_RATIO', 'LARGE_FONT_CAP_RATIO', 'ENABLE_DOT_MATRIX',
|
||||
'DOT_SPACING', 'DOT_RADIUS', 'DOT_SAFETY_BUFFER', 'CANVAS_RETRY_MAX_ROUNDS', 'CANVAS_RETRY_GROWTH',
|
||||
'DARK_COLOR_PALETTE', 'LIGHT_COLOR_PALETTE', 'FONT_COLOR', 'MAX_ATTEMPTS', 'OUTPUT_DIR', 'OUTPUT_PREFIX',
|
||||
'N_REPETITIONS', 'TARGET_FILL_RATIO', 'SIZE_RATIO', 'PACKING_EFFICIENCY',
|
||||
'USER_MIN_FONT_SIZE', 'USER_MAX_FONT_SIZE',
|
||||
'CANVAS_RETRY_MAX_ROUNDS', 'CANVAS_RETRY_GROWTH',
|
||||
'DARK_COLOR_PALETTE', 'LIGHT_COLOR_PALETTE', 'FONT_COLOR', 'OUTPUT_DIR', 'OUTPUT_PREFIX',
|
||||
'OUTPUT_PNG', 'OUTPUT_SVG', 'DB_PATH', 'METRICS_FILE', 'SAVE_DEBUG_IMAGES', 'DEBUG_OUTPUT_DIR', 'SEED',
|
||||
'LAYOUT_ORDER_MODE', 'LAYOUT_SEED'
|
||||
'LAYOUT_SEED'
|
||||
}
|
||||
|
||||
CONFIG_ALIASES = {
|
||||
'seed': 'SEED',
|
||||
'layout_order_mode': 'LAYOUT_ORDER_MODE',
|
||||
'layout_seed': 'LAYOUT_SEED',
|
||||
'excel_path': 'EXCEL_PATH',
|
||||
'mask_image_path': 'MASK_IMAGE_PATH',
|
||||
@@ -207,16 +155,12 @@ CRITICAL_TYPE_CHECKS = {
|
||||
'FONT_FALLBACK_PATHS': (list, tuple),
|
||||
'USER_MIN_FONT_SIZE': (int, float, type(None)),
|
||||
'USER_MAX_FONT_SIZE': (int, float, type(None)),
|
||||
'MAX_ATTEMPTS': int,
|
||||
'SAVE_DEBUG_IMAGES': bool,
|
||||
'REMOVE_DUPLICATES': bool,
|
||||
'ENABLE_STROKE_WEIGHTS': bool,
|
||||
'CANVAS_RETRY_MAX_ROUNDS': int,
|
||||
'CANVAS_RETRY_GROWTH': (int, float),
|
||||
'LOG_WEIGHT_RATIO': (int, float),
|
||||
'RANK_WEIGHT_RATIO': (int, float),
|
||||
'SEED': (int, type(None)),
|
||||
'LAYOUT_ORDER_MODE': str,
|
||||
'LAYOUT_SEED': (int, type(None)),
|
||||
}
|
||||
|
||||
@@ -227,7 +171,6 @@ def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Efficient WordCloud generator")
|
||||
parser.add_argument("--config", type=str, help="JSON 配置文件路径")
|
||||
parser.add_argument("--seed", type=int, help="随机种子(可复现)")
|
||||
parser.add_argument("--layout-order-mode", type=lambda s: s.upper(), choices=VALID_LAYOUT_ORDER_MODES, help="布局顺序模式")
|
||||
parser.add_argument("--layout-seed", type=int, help="布局顺序随机种子")
|
||||
parser.add_argument("--excel-path", type=str, help="Excel 输入路径")
|
||||
parser.add_argument("--mask-image-path", type=str, help="掩膜图片路径(IMAGE 模式)")
|
||||
@@ -298,7 +241,6 @@ def apply_json_config(config_path):
|
||||
def apply_cli_overrides(args):
|
||||
mapping = {
|
||||
'seed': 'SEED',
|
||||
'layout_order_mode': 'LAYOUT_ORDER_MODE',
|
||||
'layout_seed': 'LAYOUT_SEED',
|
||||
'excel_path': 'EXCEL_PATH',
|
||||
'mask_image_path': 'MASK_IMAGE_PATH',
|
||||
@@ -342,7 +284,7 @@ def _resolve_font_path(configured_path, fallback_paths, *, role):
|
||||
def finalize_runtime_config():
|
||||
global EXCEL_PATH, MASK_IMAGE_PATH, MASK_FONT_PATH, WC_FONT_PATH
|
||||
global OUTPUT_DIR, OUTPUT_PNG, OUTPUT_SVG, DB_PATH, METRICS_FILE, DEBUG_OUTPUT_DIR, MIN_FONT_SIZE
|
||||
global LAYOUT_ORDER_MODE, LAYOUT_SEED
|
||||
global LAYOUT_SEED
|
||||
|
||||
# 运行时派生字段
|
||||
MIN_FONT_SIZE = int(MIN_READABLE_HEIGHT_PX * WORK_SCALE)
|
||||
@@ -350,11 +292,6 @@ def finalize_runtime_config():
|
||||
if LAYOUT_SEED is None:
|
||||
LAYOUT_SEED = SEED
|
||||
|
||||
LAYOUT_ORDER_MODE = str(LAYOUT_ORDER_MODE).upper()
|
||||
if LAYOUT_ORDER_MODE not in VALID_LAYOUT_ORDER_MODES:
|
||||
print(f"错误: 不支持的 LAYOUT_ORDER_MODE: {LAYOUT_ORDER_MODE}")
|
||||
sys.exit(1)
|
||||
|
||||
EXCEL_PATH = str(_resolve_path(EXCEL_PATH))
|
||||
MASK_IMAGE_PATH = str(_resolve_path(MASK_IMAGE_PATH))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user