Skip to content

Fix scaled cube outline seams (1.21.11) 修复缩放几何的轮廓接缝 - #265

Merged
PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21.11from
WhereisFff:codex/cube-scale-1.21.11
Nov 11, 2026
Merged

PigeonNian merged 1 commit into
Anvil-Dev:dev/1.21.11from
WhereisFff:codex/cube-scale-1.21.11

Conversation

@WhereisFff

Copy link
Copy Markdown
Contributor

为 cube 选择几何增加每格坐标单位参数,使放大后的薄层几何按实际尺寸隐藏微小接缝。保留原有默认调用方式,并同步换算投影距离容差。

已通过本版本 cube 模块测试和 JAR 构建,覆盖严格阈值、模型浮点边界、相机变换与部分线段保留。

- 为选择几何增加每格坐标单位参数,保留默认方块坐标调用方式

- 按尺度换算接缝距离与投影容差,保留薄层几何和部分可见线段

- 覆盖缩放、严格阈值、模型浮点边界与相机变换回归
@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
cube ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering ✅ Compatible 0
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
math ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
ui ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308 Gugle2308 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论:审查通过。三份文件与 #264(1.21.8)在 head 上逐字节相同,容差换算的区分正确。1.21.11 比 #263/#264 多出一处运行期构造 SelectionGeometry 的调用点,已确认它不构成缺陷。

本次审查对应 head 38c21f25(dev/1.21.11 @ 1da1a858),3 文件 +91/−17,与 API 的 changed_files=3 / additions=91 / deletions=17 和 grep -c "^diff --git"=3 一致;三个文件的 index <old>..<new> 与实际 blob 对得上(461c450e8..b9838477b / 720e24d3c..ee6be8e8f / 2a9b5efb0..d546072d3)。state=open、draft=false、merged=false;CI build / build ✅;除 Roseau 报告(cube ✅ 0 breaking)外无人工评论。

✅ 一、与 #264 的关系:head 上三文件逐字节相同

在 git 树层面对 265 head 与 264 head 做 diff:

OutlineVisibility.java       IDENTICAL
SelectionGeometry.java       IDENTICAL     ← 与 #264 相同,已是 jspecify 版
OutlineVisibilityTest.java   IDENTICAL

1.21.8 与 1.21.11 的 module.cube/geometry 包七个文件全部逐字节相同(ConvexShape / OutlineBuilder / OutlineCache / OutlineVisibility / PackedOutline / SelectionGeometry / package-info),所以 #264 与 #265 是同一份改动落在结构相同的两条线上,两 PR 的 patch-diff 归一化后也完全一致(同 md5 0857c90e…)。

七线分组维持上一轮结论:

#262 (1.21.1)   base=ee3f6d12e→a3ddb8ab9   (jetbrains @Nullable)
#263 (1.21.4)   同上
#264~#268       720e24d3c→ee6be8e8f        (jspecify @Nullable)

✅ 二、容差换算的划分逐行核对正确

位置 语义 应缩放 实测
OutlineVisibility.java:28-29 OFFSET/EPSILON × unitsPerBlock 几何单位 已乘 ✓
:92、:109 abs(a) <= distanceEpsilon plane·(point−camera) 几何单位 已换算 ✓
low/high = min∓epsilon、clipShape 的 allowance 与 normal·segment.start() 同量纲 几何单位 用 epsilon ✓
:214 Interval.valid() = max−min > EPSILON 线段参数 t ∈ [0,1] 不缩放 原值 ✓
:189 merge 的 next.min ≤ cur.max + EPSILON 参数域 不缩放 原值 ✓
:145 append 的 high−low ≤ EPSILON 参数域 不缩放 原值 ✓
:111 direction > EPSILON 归一化向量点积(无量纲) 不缩放 原值 ✓
:101 side.lengthSqr() > 0.5 归一化成功与否 无量纲 原值 ✓

若把参数域或余弦容差一并乘缩放,scale=128 时会把有效区间判无效、把近平共面误判为左右覆盖 —— 这一划分是本 PR 的成败点,结论正确。阈值语义从绝对 0.1/16 提升为「0.1/16 × 每格单位数」,在方块坐标系下保持不变。

测试 :142-159 用 assertEquals(offset >= 0.1d/16, covers(...)) 复刻相对阈值作期望,覆盖 6 档偏移 × 0.5/1/128 三倍率并复核相机逆变换;:162-173 用 128 倍验证部分线段保留;:176-181 覆盖 0/-1/NaN/+∞。

✅ 三、1.21.11 多出的一处运行期构造点:已核实不构成缺陷

本线比 #263/#264 多一个调用点(后两条线没有):

// CubeHighlightEvents.java:39-40
PackedOutline fallback = segments > SelectionGeometry.MAX_OUTLINE_SEGMENTS
    ? new SelectionGeometry(List.of(ConvexShape.box(target.bounds()))).fallbackOutline() : null;

这是线段超限时的「整块包围盒」降级路径,每帧可能构造一次 SelectionGeometry。三点核实后判定无害:

  1. 只取 fallbackOutline()(SelectionGeometry:57 返回 PackedOutline.of(edges),不带 visibility),forView 在 PackedOutline.forView 里遇 visibility == null 直接返回自身 ⇒ 接缝逻辑与 unitsPerBlock 完全不参与;
  2. 即便走 OutlineBuilder.build,单 shape 时 OutlineVisibility.build 的 i<j 配对循环无组合 ⇒ any=false ⇒ shapes 传 List.of()、forView 首行早退,epsilon 无消费方;
  3. 该几何恒为 scale=1,与 target.bounds() 的真实尺度一致(bounds 本身就是方块坐标)。

⇒ 不需要为本 PR 补 unitsPerBlock 参数。只是提示:若将来把缩放引入主路径,这处「按 bounds 新建 scale=1 几何」会与主路径口径不同 —— 但因只画包围盒,语义上仍正确。

⚠️ 四、库内无生产调用方传入缩放,标题所述修复在库内没有可修现场

265 head 全树构造点(git grep "new SelectionGeometry("):

ModelSelections.java:120                        new SelectionGeometry(shapes)               // 1 参 → scale=1(唯一喂给 CubeSelection 的主路径)
CubeHighlightEvents.java:40                     new SelectionGeometry(List.of(box(...)))    // 1 参 → scale=1(超限降级)
module.test/PortVerification.java:76,78         new SelectionGeometry(List.of(...))         // 1 参 → scale=1
OutlineVisibilityTest.java:147,167,179          2 参(新增测试,唯二传 scale 处)

全树 scaling( 只出现在新增测试(:146、:166)与无关的 FlexibleTestScreen:155。ModelSelections(:32 的 shared 键、:114-123 的 intern)仍是 1 参;CubeModelDecoder 把模型坐标 /16.0 归一到方块坐标 ⇒ 实际几何一律 unitsPerBlock == 1。

真实交付是「让下游接入方能把缩放烘焙进几何并保持相对阈值」。建议在描述中点名接入方,或标注「为后续接入做准备」,避免日后按标题回溯时产生「为什么没效果」的疑问。

⚠️ 五、ModelSelections.shared 的 intern 键不含缩放

// ModelSelections.java:32
private final Map<List<ConvexShape>, SelectionGeometry> shared = new HashMap<>();
// :114-123
SelectionGeometry existing = this.shared.get(shapes);        // 键只有 shapes 列表
SelectionGeometry geometry = new SelectionGeometry(shapes);  // 恒 scale=1
this.shared.put(geometry.shapes(), geometry);

List<ConvexShape> 走值相等(ConvexShape.equals),键按内容匹配。今天 intern 恒传 1 参,无影响;但新公开构造器把「带缩放几何」变成可达输入 —— 一旦将来有人把缩放几何送进 intern(最自然的接入方式),第二个不同 scale 的请求会静默复用 scale=1 实例,接缝阈值退回绝对值且无任何告警。

建议 intern(List<ConvexShape>, double unitsPerBlock) 并入键(如 record GeometryKey(List<ConvexShape>, double)),或在 intern 上方注明「只接受 block 坐标几何」。附正面确认:OutlineCache.ready 是 LinkedHashMap<SelectionGeometry, …>(:20)、pending 是 IdentityHashMap(:21),而 SelectionGeometry 未覆写 equals/hashCode(grep 计数 0)⇒ 缓存按身份做键,同 shapes 不同 scale 的两个实例不会串味。

💡 六、其余(非阻塞)

  1. OutlineBuilder 的四个缩放常量未随之调整(:18-21 的 GEOMETRY_EPSILON / LINE_EPSILON / SAMPLE_DISTANCE / MERGE_EPSILON;该文件里 unitsPerBlock 引用数 = 0)。它们属于「棱线生成/合并」侧的绝对几何单位阈值,scale=128 时换算回方块空间变紧 128 倍(1e-7 → 7.8e-10 block)。新增测试在 128 倍下实测通过(covers(outline, seam) 为真 ⇒ 棱线确实生成了),当前是经验安全而非不变量保证。建议加注释说明「本类容差为绝对几何单位、不随 unitsPerBlock 缩放」。
  2. 拾取侧容差不随缩放变化:SelectionGeometry.clip 的 1.0E-9 / 1.0E-12 / 1.0E-10(:82、:84-85、:92、:105-111)保持原值,经 SelectionPart 逆变换后世界空间等效容差随缩放放大。属既有行为、本 PR 未改,但既然确立了「几何可处于任意缩放」,建议在 unitsPerBlock 的 javadoc(:27-31)补后半句「不影响射线拾取精度」——作者已写了「只影响轮廓接缝容差」的前半句。
  3. 参数校验顺序变化:2 参构造器把 unitsPerBlock 校验(:33)放在 shapes 校验(:37)之前,两参数同时非法时抛缩放错误。无害,仅记录。
  4. unitsPerBlock() 为包私有(:51-52,无修饰符)而类是 public:外部(AnvilCraft)能传值但读不回,SelectionPart 也无承载字段,无法自检「几何缩放与变换矩阵是否匹配」。若预期外部接入,考虑公开或提供 SelectionPart 侧校验入口。
  5. 测试可补两处:①「同 shapes、不同 unitsPerBlock 不共享实例」锁住 §五;② Double.MIN_VALUE / 1e300 下 inflate(offset) 的边界行为(当前只校验 finite && > 0)。

✅ 七、测试执行链路确认

1.21.11 与 1.21.8 同属「16 行 module.cube/build.gradle」形态(1.21.4 是 293 行),接线完整:

  • :15 sourceSets.test.java.setSrcDirs(['src/regressionTest/java'])、:11-13 test { useJUnitPlatform() }、:1-5 JUnit 5 (junit-bom:5.10.2);
  • :8 neoForge { addModdingDependenciesTo(sourceSets.test) };
  • module.gradle 未禁用 test(enabled = false 只作用于 machete),默认 build → check → test 成立;
  • gradle/scripts/ci.gradle:21 ciBuild 依赖 :${target.path}:build ⇒ 新增 3 个测试确实在 CI 中执行,与 build / build ✅ 相互印证。

estimatedBytes 从 160 增至 168(SelectionGeometry:47)、从 64 增至 72(OutlineVisibility:83),与新增一个 double 字段一致(+8 字节)。这两个数值同时被 OutlineCache(:43/47/49/69/76/82/85)与 MAX_GEOMETRY_BYTES(ModelSelections:28)的预算判定消费,因此 +8 是必要的精度修正。

验证边界

未编译、未运行 Gradle(Cube 模块依赖 MC/JOML 运行时)。结论基于 head 源码逐行核对 + 1.21.11/1.21.8 两条 base 的 blob 与全包内容对账 + 七线 patch-diff 归一化比对 + 匿名 API 状态/check-runs。§四「无生产消费者」由本仓库全树 new SelectionGeometry( 与 scaling( grep 得出;若接入点在 AnvilCraft(另一仓库),该判断需相应调整——请作者确认预期接入方。

@PigeonNian
PigeonNian merged commit b293284 into Anvil-Dev:dev/1.21.11 Nov 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants