Appearance
四摇盖胶封盒几何(rectangular 族)
唯一几何真源:packages/dieline/src/rectangular.mjs。TypeScript 包装器 packages/dieline/src/boxes/rectangular.ts、 网页副本 docs/demo/assets/runtime/rectangular.mjs、Python 建稿脚本都用它 —— 由 tools/build-runtime.mjs 复制生成, 不要分别维护三份规则。
这是什么结构
四侧板 + 上下各四摇盖 + 一个胶舌的胶封折叠纸盒(RSC 类),成型靠胶舌涂胶或胶带封口。 它不是磁吸灰板盒,也不是插舌锁底盒 —— 早期文档误标过,已在 docs/VERIFICATION-V1-V2.md 更正。
参数
| 参数 | 含义 | 约束 | 默认 |
|---|---|---|---|
width | 盒宽 W(front/right/back/left 中 front 的宽) | 0 < x ≤ 2000 mm | 必填 |
height | 盒高 H(侧板高) | 0 < x ≤ 2000 mm | 必填 |
depth | 盒深 D | 0 < x ≤ 2000 mm | 必填 |
glueMm | 胶舌宽 G | 0 < x ≤ 40 mm | 不一致,必须显式传:zod 包装器 10、rectangular.mjs 15 |
name | 盒型名(进 dielineId) | ≤80 | rectangular |
展开公式
text
摇盖高 flap = D / 2
展开宽 W = 2 × (width + depth) + glueMm
展开高 H = height + 2 × flap = height + depth例:120 × 260 × 90、glueMm = 15 → flap = 45、435 × 350 mm(与 docs/DEMO.md 演示口径一致); 80 × 120 × 50、glueMm = 15 → flap = 25、275 × 170 mm、13 块面板(3D 预览与折叠测试用的就是这组)。
flap = D / 2 不只决定纸面高度,它还决定盒顶怎么闭合:前后摇盖各伸 D/2、横跨整个盒宽,在中线相遇把顶面 盖满;左右摇盖各伸 D/2 压在它们下面 → 顶面是"两端 doubleLayerMm = min(W, D) 双层 + 中间 singleLayerMm = max(0, W − D) 单层"的台阶,不是一条宽 = 宽 − 深的开口。 折不动时 deriveBox 会抛 四盖无法在中线闭合,而不是折出个漏顶的盒子。 完整口径与渲染层的共面处理见 摇盖闭合:双层台阶,不是开口。
实跑复现(零依赖,仓库根目录执行):
sh
node --input-type=module -e "
const m = await import('./packages/dieline/src/rectangular.mjs');
const g = m.rectangularGeometry({ width: 120, height: 260, depth: 90, glueMm: 15 });
console.log(g.sheetMm, g.panels.length, g.edges.length);
"
# → { width: 435, height: 350 } 13 35面板布局(13 块,faceIndex 即生成顺序)
x 从 glueMm 起累加四侧板;侧板 y 范围 [flap, flap + H];顶盖贴顶边、底盖接在侧板下方。
| faceIndex | panelId | role | 120×260×90/G15 时的 x, y, w, h(mm) |
|---|---|---|---|
| 0 | front | side | 15, 45, 120, 260 |
| 1 | front_top | top | 15, 0, 120, 45 |
| 2 | front_bottom | bottom | 15, 305, 120, 45 |
| 3 | right | side | 135, 45, 90, 260 |
| 4 | right_top | top | 135, 0, 90, 45 |
| 5 | right_bottom | bottom | 135, 305, 90, 45 |
| 6 | back | side | 225, 45, 120, 260 |
| 7 | back_top | top | 225, 0, 120, 45 |
| 8 | back_bottom | bottom | 225, 305, 120, 45 |
| 9 | left | side | 345, 45, 90, 260 |
| 10 | left_top | top | 345, 0, 90, 45 |
| 11 | left_bottom | bottom | 345, 305, 90, 45 |
| 12 | glue_flap | glue | 0, 45, 15, 260 |
线是怎么定出来的(不是手画的)
- 每块侧板先写两条横向折线:
y = flap(顶)与y = flap + H(底),加一条纵向折线在面板左边缘x = 面板左边。 合计 12 条 crease(4 侧板 × 3)。 - 把这些折线的端点对(排序后取 key)放进
hinges集合。 - 遍历 13 块面板的 4 条边:不在
hinges里的边自动变成cut,并用 Map 去重 → 22 条 cut。 所以相邻摇盖之间的切缝、摇盖外沿、胶舌外沿都是这套规则推导出来的,而不是逐条手写。 - 最后追加 1 条 glue:竖直线
x = glueMm / 2,上下各内缩min(2, height / 4)mm (120×260×90/G15 →x = 7.5,从y = 47到y = 303)。
edge 总数 = 22 cut + 12 crease + 1 glue = 35。types.ts 只要求 edges ≥ 4,具体数量由几何决定。
为什么四侧板自己没有 cut
front 的四条边(上折线、下折线、左右两条纵折线)都在 hinges 里,所以侧板不产出任何 cut —— 这正是"侧板被四周摇盖和折缝完全连接"的正确拓扑。想验证:改参数跑上面那条命令,再按 panelId 分组数一遍。
面板 ↔ UV 映射
geometryMappings(g) 对每块面板取 bbox,产出 mappings:
text
dielineRectMm = bbox(panel)
uvRect = { x/W, y/H, w/W, h/H }
faceIndex = panel.faceIndex
uvRotateQuarter / mirrorU = 面板上的值(当前恒为 0 / false)applyGeometry(design, params) 除写 mappings 外还会:
- 把带
panelId的图层按"面板旧 bbox → 新 bbox"平移transform.x/y(改尺寸时内容跟着面板走); - 用
cut/crease/glue重建三个aux引导层,并放在图层数组最前; - 用
sheetMm覆盖design.canvasMm,并回写dieline.paramsMm(glueMm缺省补 15)。
这就是本站点示例里 canvasMm 与 paramsMm 必须自洽的原因,check.mjs 会按公式回算一遍。
结构线之外的东西(都没有)
纸厚折放补偿、圆角半径、工艺缝隙、防尘翼(dust)、插舌(tuck)、锁底、异形刀线 —— 均未实现。 role 枚举预留了 tuck / dust,但生成器不会产出。归属见 能力边界。