Skip to content

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

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

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

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
font ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
space-select ✅ Compatible 0
util ✅ 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.

结论:审查通过。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 五份互相同为 md5 0857c90e07a0a9b90cbab42a88eba560;
  • 两组之间只差第 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 坐标几何。

💡 五、其余(非阻塞)

  1. OutlineBuilder 的四个常量未随之缩放(:18-21 的 GEOMETRY_EPSILON / LINE_EPSILON / SAMPLE_DISTANCE / MERGE_EPSILON,该文件 unitsPerBlock 引用数 = 0)。它们是「棱线生成/合并」侧的绝对几何单位阈值,scale=128 时换算回方块空间会紧 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 未改,但既然本 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)。已有 rejectsInvalidCoordinateScales(0/-1/NaN/+∞)覆盖度不错。

✅ 六、测试接线与 CI 确认

  • 1.21.4 base 的 module.cube/build.gradle:292 为 sourceSets.test.java.setSrcDirs(['src/regressionTest/java'])、:288-290 useJUnitPlatform(),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(另一仓库),该判断需相应调整——请作者确认预期接入方。

@PigeonNian
PigeonNian merged commit 09d928b into Anvil-Dev:dev/1.21.4 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