Repository navigation
Fix scaled cube outline seams (1.21.4) 修复缩放几何的轮廓接缝 - #263
Conversation
- 为选择几何增加每格坐标单位参数,保留默认方块坐标调用方式 - 按尺度换算接缝距离与投影容差,保留薄层几何和部分可见线段 - 覆盖缩放、严格阈值、模型浮点边界与相机变换回归
🌿 Roseau API Breaking Change Report
|
Gugle2308
left a comment
There was a problem hiding this comment.
结论:审查通过。unitsPerBlock 的换算范围划分正确,几何单位容差按缩放折算、参数域和无量纲判据保持原值;但它在本仓库内没有生产调用方传入非 1 的缩放,因此对 1.21.4 现有行为没有影响。本 PR 与 #262 属于逐字节同源,建议 7 条线按组同步合并。
本次审查对应 head 0f310864(dev/1.21.4 @ 464a352c),3 个文件 +91/−17,与 API 的 changed_files=3 / additions=91 / deletions=17 以及 grep -c "^diff --git"=3 三项一致;三个文件的 index <old>..<new> 与 git rev-parse 得到的实际 blob 逐一对上(461c450e8..b9838477b / ee3f6d12e..a3ddb8ab9 / 2a9b5efb0..d546072d3)。state=open、draft=false、merged=false;CI build / build ✅ success;除 Roseau API 报告(cube ✅ 0 breaking,本次属纯新增,符合预期)外,尚无人工评论。
✅ 一、容差换算的划分正确对应「该乘 / 不该乘」
OutlineVisibility 中两类容差的划分是本次改动的关键,逐行核对后裁定正确:
| 位置 | 语义 | 应为 | 实测 |
|---|---|---|---|
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 时把有效区间判为无效,并把接近共面的平面误判为左右覆盖。阈值语义从绝对 0.1/16 提升为「0.1/16 × 每格单位数」,在方块坐标系下保持不变。
新增测试 :142-159 用 assertEquals(offset >= 0.1d/16, covers(...)) 直接以相对阈值作为期望值,覆盖 SMALL、0.09999/16、0.1/16、0.10001/16、(float)16.1/16−1、0.2/16 六档 × 0.5/1/128 三倍率,并复核了相机逆变换(localPosition)后的可见性;:162-173 用 128 倍验证部分线段保留。这正是本 PR 的核心声称,测试判据对应正确,而不是只做冒烟验证。
✅ 二、七线同源对账:仅有一个 index 行差异,且归因明确
本仓库现有 7 条同标题 PR:#262(1.21.1)、#263(1.21.4)、#264(1.21.8)、#265(1.21.11)、#266(26.1)、#267(26.2)、#268(26.3),全部 open。
#262与#263的 diff:去掉index行后逐字节相同;#264~#268五份互相同为 md50857c90e07a0a9b90cbab42a88eba560;- 两组之间只差第 129 行,即
SelectionGeometry.java的index行:ee3f6d12e..a3ddb8ab9↔720e24d3c..ee6be8e8f。
差异归因已查实,不是内容漂移,请勿据此判不一致:
1.21.1 base blob = ee3f6d12e ← import org.jetbrains.annotations.Nullable
1.21.4 base blob = ee3f6d12e ← 同上(两线该文件 md5 全等)
1.21.8 base blob = 720e24d3c ← import org.jspecify.annotations.Nullable
26.1 base blob = 720e24d3c ← 同上
即 {1.21.1, 1.21.4} 与 {1.21.8, 1.21.11, 26.1, 26.2, 26.3} 的分界恰好是空值注解迁移(@Nullable 从 jetbrains 迁到 jspecify),是各分支既有状态,本 PR 未碰 import 行。OutlineVisibility.java 在 1.21.4 与 26.1 两条 base 上完全一致(diff 空),所以七线逻辑是同一份。建议维护者按组一起合并;只合一组会让另外几个版本线缺同一修复。
⚠️ 三、库内没有生产路径传入缩放,标题描述的修复在仓库内没有可修现场
当前 head 全树 new SelectionGeometry( 只有下列调用点:
ModelSelections.java:124 new SelectionGeometry(shapes) // 1 参 → scale=1(唯一生产调用)
module.test/.../PortVerification.java:75,77 new SelectionGeometry(List.of(...)) // 1 参 → scale=1
OutlineVisibilityTest.java:147,167,179 2 参(新增测试,唯二传 scale 的地方)
ModelSelections.bake → intern → :124 是唯一喂给 CubeSelection 的入口;CubeModelDecoder.decode 把模型元素坐标 /16.0 归一到 block 坐标(:39-40),SelectionPart 的旋转/镜像走变换矩阵、不烘焙进几何。因此七条线的实际几何一律 unitsPerBlock == 1,本次改动对现网渲染/拾取结果零差异。
「Fix scaled cube outline seams」在库内没有现场可修,交付的其实是「让下游接入方有能力把缩放烘焙进几何并保持相对阈值」。这不阻塞合入,但建议二选一:① 在描述里点名接入方(AnvilCraft 侧哪个调用点会传非 1 值);② 标注为「为后续接入做准备」,便于 changelog 与 issue 归属判断。否则事后按标题回溯会遇到「这个修复为什么没有效果」的疑问。
⚠️ 四、ModelSelections.shared 的 intern 键不含缩放
// ModelSelections.java:35
private final Map<List<ConvexShape>, SelectionGeometry> shared = new HashMap<>();
// :118-127
SelectionGeometry existing = this.shared.get(shapes);
if (existing != null) return existing; // ← 键只有 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 的实例,接缝阈值退回绝对 0.1/16,且没有任何日志或异常。
建议 intern(List<ConvexShape>, double unitsPerBlock) 并把它并入键(如 record GeometryKey(List<ConvexShape> shapes, double units)),或在 intern 上加一行注释,明确只接受 block 坐标几何。
💡 五、其余(非阻塞)
OutlineBuilder的四个常量未随之缩放(:18-21的GEOMETRY_EPSILON/LINE_EPSILON/SAMPLE_DISTANCE/MERGE_EPSILON,该文件unitsPerBlock引用数 = 0)。它们是「棱线生成/合并」侧的绝对几何单位阈值,scale=128 时换算回方块空间会紧 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 未改,但既然本 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)。已有rejectsInvalidCoordinateScales(0/-1/NaN/+∞)覆盖度不错。
✅ 六、测试接线与 CI 确认
- 1.21.4 base 的
module.cube/build.gradle:292为sourceSets.test.java.setSrcDirs(['src/regressionTest/java'])、:288-290useJUnitPlatform(),gradle/scripts/ci.gradle:21的ciBuild依赖:module.cube:build(→check→test)⇒ 新增 3 个测试确实在 CI 中执行,与build / build✅ 相互印证。 estimatedBytes从160增至168(SelectionGeometry:47)、从64增至72(OutlineVisibility:83),均与新增一个double字段一致(+8 字节,不依赖 JVM 字段打包)。这两个数值同时被OutlineCache(:43/47/49/69/76/82/85)与MAX_GEOMETRY_BYTES预算消费,因此 +8 是必要的精度修正,不是随意加的。OutlineCache.ready是LinkedHashMap<SelectionGeometry, …>,而SelectionGeometry未覆写equals/hashCode,所以按身份做键 ⇒ 同 shapes 不同 scale 的两个实例在缓存侧不会相互串味。
验证边界
未编译、未运行 Gradle(Cube 模块依赖 MC/JOML 运行时)。结论基于 head 源码逐行核对 + 1.21.4/1.21.1/1.21.8/26.1 四条 base 的 blob 与内容对账 + 7 份 patch-diff 归一化比对 + 匿名 API 状态/check-runs。§三「无生产消费者」由本仓库全树 new SelectionGeometry( 与 scaling( grep 得出;若接入点在 AnvilCraft(另一仓库),该判断需相应调整——请作者确认预期接入方。
为 cube 选择几何增加每格坐标单位参数,使放大后的薄层几何按实际尺寸隐藏微小接缝。保留原有默认调用方式,并同步换算投影距离容差。
已通过本版本 cube 模块测试和 JAR 构建,覆盖严格阈值、模型浮点边界、相机变换与部分线段保留。