Repository navigation
Fix scaled cube outline seams (1.21.8) 修复缩放几何的轮廓接缝 - #264
Conversation
- 为选择几何增加每格坐标单位参数,保留默认方块坐标调用方式 - 按尺度换算接缝距离与投影容差,保留薄层几何和部分可见线段 - 覆盖缩放、严格阈值、模型浮点边界与相机变换回归
🌿 Roseau API Breaking Change Report
|
Gugle2308
left a comment
There was a problem hiding this comment.
结论:审查通过。本 PR 与 #263 的内容除 @Nullable 注解迁移差异外逐字节相同,容差换算的划分正确;但对 1.21.8 现有行为没有实际改变(本仓库内没有生产调用方传入非 1 的缩放),建议七个版本线按组同步合并。
本次审查对应 head ee511320(dev/1.21.8 @ b12849a8),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)外没有人工评论。
✅ 一、与 #263 的关系:三项内容只有注解 import 不同
在 git 树层面直接对两个 head 的三份文件做 diff:
OutlineVisibility.java IDENTICAL
OutlineVisibilityTest.java IDENTICAL
SelectionGeometry.java 仅 5 行:org.jetbrains.annotations.Nullable → org.jspecify.annotations.Nullable
#263(1.21.4) 与 #264(1.21.8) 的 patch-diff 只差第 129 行的 index(ee3f6d12e..a3ddb8ab9 ↔ 720e24d3c..ee6be8e8f),这与 Nullable 迁移完全对应——1.21.8 线的 package-info.java 已经是 @NullMarked(jspecify),1.21.4 线仍是 @FieldsAreNonnullByDefault/@MethodsReturnNonnullByDefault/@ParametersAreNonnullByDefault(javax + mojang)。本 PR 未改动 import 行,因此七线是同一份逻辑。
七线 head 的 SelectionGeometry blob 分为两组,与上面结论一致:
#263 (1.21.4) a3ddb8ab9
#264~#268 ee6be8e8f ← 1.21.8 / 1.21.11 / 26.1 / 26.2 / 26.3 完全相同
建议维护者按这两组分别合并;如果只合并一组,另外几个版本线就会缺少同一处修复。
✅ 二、容差换算范围按应缩放与不应缩放划分,逐行核对正确
| 位置 | 语义 | 应缩放 | 实测 |
|---|---|---|---|
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 |
normalized·normalized 余弦 |
不缩放 | 原值 ✓ |
:101 side.lengthSqr() > 0.5 |
归一化相对判据 | 无量纲 | 原值 ✓ |
如果把 t 参数域或余弦容差也乘以缩放,在 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/+∞。这与声称的四项(严格阈值、模型浮点边界、相机变换、部分线段保留)对应,不是只做冒烟验证。
⚠️ 三、库内无生产调用方传入缩放,标题所述修复在库内没有实际可修场景
264 head 全树 new SelectionGeometry( 的调用点:
ModelSelections.java:120 new SelectionGeometry(shapes) // 1 参 → scale=1(唯一生产调用)
module.test/.../PortVerification:75,77 new SelectionGeometry(List.of(...)) // 1 参 → scale=1
OutlineVisibilityTest:147,167,179 2 参(新增测试,唯二传 scale 处)
全树 scaling( 只出现在新增测试(:146、:166)。1.21.8 的 ModelSelections(:32 的 shared 键、:114-123 的 intern)同样是 intern(List<ConvexShape>) → new SelectionGeometry(shapes),CubeModelDecoder 把模型坐标 /16.0 归一到 block 坐标,SelectionPart 的旋转/镜像走矩阵、不写入几何 ⇒ 实际几何一律 unitsPerBlock == 1。
本 PR 实际交付的是「让下游接入方有能力把缩放写入几何并保持相对阈值」。建议描述补一句接入方,或标注为「为后续接入做准备」,减少日后按标题回溯时的困惑。
⚠️ 四、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(该文件 :117)实现,因此键按内容匹配。今天 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 的两个实例不会相互串味,这一侧是安全的。
💡 五、其余问题(不阻塞合并)
OutlineBuilder的四个常量未随之缩放(:18-21的GEOMETRY_EPSILON/LINE_EPSILON/SAMPLE_DISTANCE/MERGE_EPSILON;该文件里unitsPerBlock引用数 = 0)。它们属于「棱线生成/合并」侧的绝对几何单位阈值,scale=128 时换算回 block 空间后严格了 128 倍(1e-7 → 7.8e-10block)。新增测试在 128 倍下对这些 fixture 实测通过(covers(outline, seam)为真 ⇒ 棱线确实生成了),所以目前是经验上安全,而不是由不变量保证。建议加注释说明「本类容差为绝对几何单位、不随unitsPerBlock缩放」。- 拾取侧容差不随缩放变化:
SelectionGeometry.clip的1.0E-9/1.0E-12/1.0E-10(:82、:84-85、:92、:105-111)保持原值,经SelectionPart逆变换后世界空间等效容差会随缩放放大。属于既有行为、本 PR 未改动,但既然确立了「几何可处于任意缩放」,建议在unitsPerBlock的 javadoc(:27-31)补后半句「不影响射线拾取精度」——作者已写了「只影响轮廓接缝容差」的前半句。 - 参数校验顺序变化:2 参构造器把
unitsPerBlock校验(:33)放在shapes校验(:37)之前,两参数同时非法时抛缩放错误。无影响,仅记录。 unitsPerBlock()为包私有(:51-52,无修饰符)而类public:外部(AnvilCraft)能传值但读不回,SelectionPart也无承载字段,无法自检「几何缩放与变换矩阵是否匹配」。若预期外部接入,考虑公开或提供SelectionPart侧的校验入口。- 测试可补两处:①「同 shapes、不同
unitsPerBlock不共享实例」锁住 §四;②Double.MIN_VALUE/1e300下inflate(offset)的边界行为(当前只校验finite && > 0)。
✅ 六、测试执行链路确认(本线接线与 1.21.4 不同,已单独核实)
1.21.8 的 module.cube/build.gradle 只有 16 行(1.21.4 是 293 行),但测试配置完整:
:15sourceSets.test.java.setSrcDirs(['src/regressionTest/java'])、:11-13test { useJUnitPlatform() }、:1-5JUnit 5 (junit-bom:5.10.2);:8neoForge { addModdingDependenciesTo(sourceSets.test) }(测试需要 MC 类型);- 根
build.gradle的subprojects统一应用java+module.gradle,module.gradle未禁用test(enabled = false只作用于machete),默认build → check → test链成立; gradle/scripts/ci.gradle:21ciBuild依赖:${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.8/1.21.4 base 的 blob 与内容对账 + 七线 patch-diff 归一化比对 + 匿名 API 状态/check-runs。§三「无生产消费者」由本仓库全树 new SelectionGeometry( 与 scaling( grep 得出;若接入点在 AnvilCraft(另一仓库),该判断需相应调整——请作者确认预期接入方。
为 cube 选择几何增加每格坐标单位参数,使放大后的薄层几何按实际尺寸隐藏微小接缝。保留原有默认调用方式,并同步换算投影距离容差。
已通过本版本 cube 模块测试和 JAR 构建,覆盖严格阈值、模型浮点边界、相机变换与部分线段保留。