Skip to content

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

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

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

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.

结论:审查通过。本 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 的两个实例不会相互串味,这一侧是安全的。

💡 五、其余问题(不阻塞合并)

  1. OutlineBuilder 的四个常量未随之缩放(:18-21 的 GEOMETRY_EPSILON / LINE_EPSILON / SAMPLE_DISTANCE / MERGE_EPSILON;该文件里 unitsPerBlock 引用数 = 0)。它们属于「棱线生成/合并」侧的绝对几何单位阈值,scale=128 时换算回 block 空间后严格了 128 倍(1e-7 → 7.8e-10 block)。新增测试在 128 倍下对这些 fixture 实测通过(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.4 不同,已单独核实)

1.21.8 的 module.cube/build.gradle 只有 16 行(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) }(测试需要 MC 类型);
  • 根 build.gradle 的 subprojects 统一应用 java + module.gradle,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.8/1.21.4 base 的 blob 与内容对账 + 七线 patch-diff 归一化比对 + 匿名 API 状态/check-runs。§三「无生产消费者」由本仓库全树 new SelectionGeometry( 与 scaling( grep 得出;若接入点在 AnvilCraft(另一仓库),该判断需相应调整——请作者确认预期接入方。

@PigeonNian
PigeonNian merged commit 6a0d935 into Anvil-Dev:dev/1.21.8 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