From 83de32975083e3dd2d00aa3fcfd3b4739baf3f3a Mon Sep 17 00:00:00 2001 From: fukunee Date: Sat, 19 Sep 2026 04:26:55 +0800 Subject: [PATCH] Add stress test generator for 1000 synthetic entries Adds scripts/gen_entries_stress.py to produce a large synthetic dataset for performance testing, and notes the texture lazy-loading plan in the README. --- README.md | 1 + scripts/gen_entries_stress.py | 114 ++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 scripts/gen_entries_stress.py diff --git a/README.md b/README.md index a3e9c99..2a1d014 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ scripts/ 截图与验证辅助(开发用) ## TODO / 后续规划 +- [ ] **纹理懒加载**:书页与时间轴卡片的 Canvas 纹理改为按需 / 增量生成。当前启动时一次性生成全部纹理(每条词条正反两面),在 1000 条词条压力测试下启动耗时约 6 秒;懒加载后应降到约 1 秒。 - [ ] **活页本分册**:为全部词条增加分册(分卷)机制——按历史时期或主题分组,顶部提供分册导航与切换,活页本只展示当前分册的内容,降低单册厚度、便于浏览。 - [ ] **时间轴时期划分**:时间轴轴线上标注中西方历史时期刻度(如中国朝代:夏商周 / 秦汉 / 隋唐 / 宋元明清;西方:古典时代 / 中世纪 / 文艺复兴 / 启蒙运动 / 工业革命等),作为背景衬托。 - [ ] **词条插入时间轴**:词条按其 `timeNumeric` 真实时间位置插入时间轴(当前为等距排列),使时期刻度与词条位置相互对应。 diff --git a/scripts/gen_entries_stress.py b/scripts/gen_entries_stress.py new file mode 100644 index 0000000..0d754f9 --- /dev/null +++ b/scripts/gen_entries_stress.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +"""生成 1000 条合成词条,用于压力测试。 + +与 gen_entries.py 互斥:两者都写 src/data/entries.js,跑哪个就用哪份数据。 +切换回真实数据: python scripts/gen_entries.py +""" +import random +import math + +N = 1000 +random.seed(20240101) + +CATS = ['人', '事', '物'] +SKETCHES = [ + 'fossil', 'tablet', 'pyramid', 'figure', 'temple', 'crown', 'colosseum', 'scroll', + 'horseman', 'ship', 'globe', 'apple', 'machine', 'bell', 'tree', 'atom', 'dove', 'rocket', + 'book', 'bridge', 'flag', 'scale', 'sword', +] + +# 世界主要城市坐标池:制造既分散又带聚集的分布(考验重叠与覆盖) +CITY_POOL = [ + (39.9, 116.4), (31.2, 121.5), (35.7, 139.7), (37.6, 127.0), (22.3, 114.2), + (13.8, 100.5), (1.35, 103.8), (-6.2, 106.8), (21.0, 105.8), (28.6, 77.2), + (19.1, 72.9), (23.8, 90.4), (35.7, 51.4), (33.3, 44.4), (24.7, 46.7), + (41.0, 29.0), (31.8, 35.2), (30.0, 31.2), (36.8, 3.1), (33.6, -7.6), + (6.5, 3.4), (9.0, 38.7), (-1.3, 36.8), (-26.2, 28.0), (-33.9, 18.4), + (51.5, -0.1), (48.9, 2.4), (52.5, 13.4), (41.9, 12.5), (40.4, -3.7), + (38.7, -9.1), (52.4, 4.9), (50.9, 4.4), (48.2, 16.4), (55.8, 37.6), + (59.3, 18.1), (50.5, 30.5), (42.7, 23.3), (40.7, -74.0), (34.1, -118.2), + (41.9, -87.6), (37.8, -122.4), (43.7, -79.4), (19.4, -99.1), (-23.6, -46.6), + (-22.9, -43.2), (-34.6, -58.4), (-33.4, -70.7), (-12.0, -77.0), (4.7, -74.1), + (-33.9, 151.2), (-37.8, 145.0), (-36.8, 174.8), +] + +def fmt_time(tn): + if tn < 0: + a = abs(tn) + if a >= 1000000: + return f"~ BC {a:,}" + if a >= 10000: + return f"~{a:,} BC" + return f"~{a} BC" + if tn >= 10000: + return f"AD {tn:,}" + return f"AD {tn}" + +def make_year(i): + # 前 400 条:公元前(对数分布,远古稀疏、近古密集) + # 后 600 条:公元后(线性分布 1..2022) + if i < 400: + u = i / 399.0 # 0..1 + return -int(round(10 ** (u * math.log10(3.2e6)))) + u = (i - 400) / 599.0 # 0..1 + return int(round(1 + u * 2021)) + +def esc(s): + return s.replace("\\", "\\\\").replace("'", "\\'") + +entries = [] +for i in range(N): + tn = make_year(i) + cat = CATS[i % 3] + sketch = SKETCHES[i % len(SKETCHES)] + # 坐标:60% 落在城市池附近(带抖动),40% 均匀随机,制造聚集 + 分散 + if random.random() < 0.6: + base = random.choice(CITY_POOL) + lat = round(base[0] + random.uniform(-0.4, 0.4), 5) + lng = round(base[1] + random.uniform(-0.4, 0.4), 5) + else: + lat = round(random.uniform(-55, 70), 5) + lng = round(random.uniform(-180, 180), 5) + region = f"压力测试区 {i + 1:03d}" + name = f"词条 {i + 1:03d}" + desc = "这是一条用于压力测试的合成词条,用来检验在大量数据下三维翻页、时间轴与地理视图的渲染和交互性能表现。" + entries.append((fmt_time(tn), tn, region, name, cat, sketch, lat, lng, desc)) + +# 按年份升序排列,保证时间轴(按列表顺序均布)与年代一致 +entries.sort(key=lambda e: e[1]) + +lines = ["// 压力测试用合成词条数据(1000 条)", + "// timeNumeric 用于时间轴排序/定位(公元前为负,公元后为正,单位:年)", + "", + "export const CATEGORY_INFO = {", + " 人: { color: '#a03a2a', en: 'PERSONA' },", + " 事: { color: '#2c4a6b', en: 'EVENTUS' },", + " 物: { color: '#3f5c3a', en: 'ARTIFEX' },", + "};", + "", + "export const ENTRIES = ["] +for i, (time, tn, region, name, cat, sketch, lat, lng, desc) in enumerate(entries, 1): + lines.append(" {") + lines.append(f" id: 'No.{i:03d}',") + lines.append(f" time: '{esc(time)}',") + lines.append(f" timeNumeric: {tn},") + lines.append(f" region: '{esc(region)}',") + lines.append(f" name: '{esc(name)}',") + lines.append(f" category: '{esc(cat)}',") + lines.append(f" sketch: '{esc(sketch)}',") + lines.append(f" lat: {lat}, lng: {lng},") + lines.append(f" description: '{esc(desc)}',") + lines.append(" },") +lines.append("];") +lines.append("") +lines.append("// 时间轴横向定位:对称对数映射,远古压缩、近代展开") +lines.append("export function timelineX(t) {") +lines.append(" const sign = t >= 0 ? 1 : -1;") +lines.append(" const mag = Math.log10(1 + Math.abs(t) / 50);") +lines.append(" return sign * mag * 1.15;") +lines.append("}") + +with open("src/data/entries.js", "w", encoding="utf-8") as f: + f.write("\n".join(lines) + "\n") + +print(f"written entries.js with {len(entries)} entries (stress)")