@ikijs/editor 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  DeletePhysicsRig: () => DeletePhysicsRig,
34
34
  EditorDocument: () => EditorDocument,
35
35
  HEAD_BAND: () => HEAD_BAND,
36
+ LIP_ROLES: () => LIP_ROLES,
36
37
  LayerGeometryError: () => LayerGeometryError,
37
38
  SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
38
39
  SetDeformerBindings: () => SetDeformerBindings,
@@ -54,6 +55,7 @@ __export(index_exports, {
54
55
  UV_INSET_PX: () => UV_INSET_PX,
55
56
  armGeometry: () => armGeometry,
56
57
  captureBindingEndpoint: () => captureBindingEndpoint,
58
+ columnRuns: () => columnRuns,
57
59
  computeGridOffsets: () => computeGridOffsets,
58
60
  createDefaultMatrixDeformer: () => createDefaultMatrixDeformer,
59
61
  createDefaultPart: () => createDefaultPart,
@@ -68,6 +70,9 @@ __export(index_exports, {
68
70
  headHalfOf: () => headHalfOf,
69
71
  interpolateGridOffsets: () => interpolateGridOffsets,
70
72
  isSpeckCore: () => isSpeckCore,
73
+ mouthFold: () => mouthFold,
74
+ mouthOpening: () => mouthOpening,
75
+ mouthRestShift: () => mouthRestShift,
71
76
  packAtlas: () => packAtlas,
72
77
  parseLayerRoles: () => parseLayerRoles,
73
78
  partIdsOfRole: () => partIdsOfRole,
@@ -3087,6 +3092,9 @@ function forearmPoseDeformer(role, arm) {
3087
3092
  };
3088
3093
  }
3089
3094
 
3095
+ // src/auto-rig/mouth.ts
3096
+ var import_format7 = require("@ikijs/format");
3097
+
3090
3098
  // src/auto-rig/roles.ts
3091
3099
  var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
3092
3100
  var isLipRole = (role) => LIP_ROLES.includes(role);
@@ -3228,33 +3236,261 @@ function parseLayerRoles(fileNames) {
3228
3236
  return out;
3229
3237
  }
3230
3238
 
3239
+ // src/auto-rig/mouth.ts
3240
+ var UPPER_SHARE = 0.4;
3241
+ var MOUTH_KNOT_PX = 4;
3242
+ var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
3243
+ function columnRuns(layer) {
3244
+ const cols = /* @__PURE__ */ new Map();
3245
+ (layer.rowRuns ?? []).forEach((runs, k) => {
3246
+ for (let i = 0; i < runs.length; i += 2) {
3247
+ for (let c = runs[i]; c < runs[i + 1]; c++) {
3248
+ const list = cols.get(c) ?? [];
3249
+ const last = list[list.length - 1];
3250
+ if (last !== void 0 && last[1] === k) last[1] = k + 1;
3251
+ else list.push([k, k + 1]);
3252
+ cols.set(c, list);
3253
+ }
3254
+ }
3255
+ });
3256
+ return cols;
3257
+ }
3258
+ var edgesOf = (layer, run) => ({
3259
+ top: layer.canvasH / 2 - (layer.bbox.y + run[0]),
3260
+ bottom: layer.canvasH / 2 - (layer.bbox.y + run[1])
3261
+ });
3262
+ function mouthOpening(byRole) {
3263
+ const inner = byRole.get("mouth_inner");
3264
+ const upper = byRole.get("lip_upper");
3265
+ const lower = byRole.get("lip_lower");
3266
+ const innerCols = columnRuns(inner);
3267
+ if (innerCols.size === 0) {
3268
+ throw new LayerGeometryError(
3269
+ `auto-rig: layer "${inner.fileName}": mouth_inner has no opaque pixel (alpha \u2265 128) to read the opening off`
3270
+ );
3271
+ }
3272
+ const upperCols = columnRuns(upper);
3273
+ const lowerCols = columnRuns(lower);
3274
+ const cols = [...innerCols.keys()];
3275
+ const x0 = Math.min(...cols);
3276
+ const x1 = Math.max(...cols);
3277
+ const read = (col) => {
3278
+ const runs = innerCols.get(col);
3279
+ if (runs === void 0) return void 0;
3280
+ const T = edgesOf(inner, runs[0]).top;
3281
+ const Bb = edgesOf(inner, runs[runs.length - 1]).bottom;
3282
+ const line = [...upperCols.get(col) ?? []].reverse().map((r) => edgesOf(upper, r)).find((e) => e.top >= T);
3283
+ const Tu = line?.bottom ?? T;
3284
+ const lineTop = line?.top ?? T;
3285
+ const skin = lowerCols.get(col)?.[0];
3286
+ return {
3287
+ T,
3288
+ Bb,
3289
+ Tu,
3290
+ lineTop,
3291
+ lineH: lineTop - Tu,
3292
+ Bl: skin === void 0 ? Bb : edgesOf(lower, skin).top,
3293
+ H: T - Bb
3294
+ };
3295
+ };
3296
+ const reads = [];
3297
+ for (let c = x0; c <= x1; c++) {
3298
+ let r;
3299
+ for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
3300
+ reads.push(r);
3301
+ }
3302
+ const m = (x0 + x1) / 2;
3303
+ let p = 0;
3304
+ let q = 0;
3305
+ if (reads.length >= 4) {
3306
+ let s11 = 0;
3307
+ let s12 = 0;
3308
+ let s22 = 0;
3309
+ let t1 = 0;
3310
+ let t2 = 0;
3311
+ reads.forEach((r, i) => {
3312
+ const x = x0 + i;
3313
+ const b1 = (x - x0) * (x - x1);
3314
+ const b2 = b1 * (x - m);
3315
+ const d = r.T - UPPER_SHARE * r.H - r.Tu;
3316
+ s11 += b1 * b1;
3317
+ s12 += b1 * b2;
3318
+ s22 += b2 * b2;
3319
+ t1 += b1 * d;
3320
+ t2 += b2 * d;
3321
+ });
3322
+ const det = s11 * s22 - s12 * s12;
3323
+ p = (t1 * s22 - t2 * s12) / det;
3324
+ q = (s11 * t2 - s12 * t1) / det;
3325
+ }
3326
+ const columns = reads.map((r, i) => {
3327
+ const x = x0 + i;
3328
+ return { ...r, seam: r.Tu + (x - x0) * (x - x1) * (p + q * (x - m)) };
3329
+ });
3330
+ const base = (col) => columns[Math.min(x1, Math.max(x0, col)) - x0];
3331
+ const centre = Math.round((x0 + x1) / 2);
3332
+ const w = base(centre).lineH;
3333
+ return {
3334
+ x0,
3335
+ x1,
3336
+ centre,
3337
+ canvasW: inner.canvasW,
3338
+ w,
3339
+ at(col) {
3340
+ const b = base(col);
3341
+ return { ...b, overlap: Math.max(0, Math.min(w, b.lineH)) };
3342
+ }
3343
+ };
3344
+ }
3345
+ function mouthFold(role, opening) {
3346
+ return (col, y, v) => {
3347
+ if (v === 1) return 0;
3348
+ if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
3349
+ return 0;
3350
+ const c = opening.at(col);
3351
+ const k = 1 - v;
3352
+ switch (role) {
3353
+ case "lip_upper":
3354
+ return (c.seam - c.Tu) * k;
3355
+ case "lip_lower":
3356
+ return (c.seam + c.overlap - c.Bl) * k;
3357
+ case "mouth_inner":
3358
+ return (c.seam + c.overlap - y) * k;
3359
+ }
3360
+ };
3361
+ }
3362
+ function columnOfKnot(knot, opening, role) {
3363
+ const boundary = Math.round(knot + opening.canvasW / 2);
3364
+ const col = boundary === opening.x1 + 1 ? opening.x1 : boundary;
3365
+ if (col >= opening.x0 && col <= opening.x1) return col;
3366
+ return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
3367
+ }
3368
+ function mouthFoldWarp(role, mesh, xs, box, opening) {
3369
+ const fold = mouthFold(role, opening);
3370
+ return localWarp(import_format7.StandardParameter.MouthOpen, mesh, box, [0, 1], (p, v, i) => {
3371
+ const col = columnOfKnot(xs[i % xs.length], opening, role);
3372
+ return [0, col === void 0 ? 0 : fold(col, p[1], v)];
3373
+ });
3374
+ }
3375
+ function mouthRestShift(byRole) {
3376
+ const rig = buildMouthRig(byRole);
3377
+ const fields = /* @__PURE__ */ new Map();
3378
+ for (const role of ["lip_upper", "lip_lower"]) {
3379
+ const box = boxOfLayer(byRole.get(role));
3380
+ const { mesh, xs } = mouthMesh(box, rig.knots);
3381
+ const warp = mouthFoldWarp(role, mesh, xs, box, rig.opening);
3382
+ const offsets = warp.keyforms[0].offsets;
3383
+ fields.set(role, {
3384
+ box,
3385
+ xs,
3386
+ dy: xs.map((_, i) => offsets[2 * i + 1] * bh(box))
3387
+ });
3388
+ }
3389
+ const half = rig.opening.canvasW / 2;
3390
+ return (role, col) => {
3391
+ const { box, xs, dy } = fields.get(role);
3392
+ const x = col + 0.5 - half;
3393
+ if (x < box.x0 || x > box.x1) return 0;
3394
+ let k = 1;
3395
+ while (k < xs.length - 1 && xs[k] < x) k++;
3396
+ const t = (x - xs[k - 1]) / (xs[k] - xs[k - 1]);
3397
+ return dy[k - 1] + (dy[k] - dy[k - 1]) * t;
3398
+ };
3399
+ }
3400
+ function mouthKnots(union, opening) {
3401
+ const half = opening.canvasW / 2;
3402
+ const step = MOUTH_KNOT_PX;
3403
+ const xs = [union.x0];
3404
+ while (xs[xs.length - 1] + step < union.x1 - 0.3 * step)
3405
+ xs.push(xs[xs.length - 1] + step);
3406
+ xs.push(union.x1);
3407
+ const fixed = /* @__PURE__ */ new Set([union.x0, union.x1]);
3408
+ for (const x of [
3409
+ opening.x0 - half,
3410
+ opening.x1 - half,
3411
+ opening.x1 + 1 - half
3412
+ ]) {
3413
+ fixed.add(x);
3414
+ if (xs.includes(x)) continue;
3415
+ const near = xs.findIndex((v) => Math.abs(v - x) < 0.3 * step);
3416
+ if (near >= 0 && !fixed.has(xs[near])) xs[near] = x;
3417
+ else xs.push(x);
3418
+ }
3419
+ xs.sort((p, q) => p - q);
3420
+ for (const x of xs) {
3421
+ if (!Number.isInteger(x + half)) {
3422
+ throw new Error(
3423
+ `auto-rig: mouth knot ${x} is not an integer canvas boundary`
3424
+ );
3425
+ }
3426
+ }
3427
+ return xs;
3428
+ }
3429
+ function mouthMesh(box, knots) {
3430
+ const xs = [box.x0, ...knots.filter((k) => k > box.x0 && k < box.x1), box.x1];
3431
+ const rows = cellsFor(bh(box), MOUTH_KNOT_PX, 2, 40);
3432
+ const ys = Array.from(
3433
+ { length: rows + 1 },
3434
+ (_, r) => box.y1 - bh(box) * r / rows
3435
+ );
3436
+ return { mesh: columnMesh(box, { xs, ys }), xs };
3437
+ }
3438
+ function buildMouthRig(byRole) {
3439
+ const opening = mouthOpening(byRole);
3440
+ const union = unionBoxes(LIP_ROLES.map((r) => boxOfLayer(byRole.get(r))));
3441
+ return {
3442
+ opening,
3443
+ frame: mouthFrameOf(union),
3444
+ knots: mouthKnots(union, opening),
3445
+ union
3446
+ };
3447
+ }
3448
+ function mouthAnchor(byRole) {
3449
+ const box = (role) => boxOfLayer(byRole.get(role));
3450
+ if (isLipSet(byRole)) {
3451
+ const opening = mouthOpening(byRole);
3452
+ return {
3453
+ at: {
3454
+ x: cx(box("mouth_inner")),
3455
+ y: opening.at(opening.centre).seam
3456
+ },
3457
+ box: unionBoxes(LIP_ROLES.map(box))
3458
+ };
3459
+ }
3460
+ const m = box("mouth");
3461
+ return {
3462
+ at: { x: cx(m), y: cy(m) },
3463
+ box: byRole.has("mouth_open") ? unionBoxes([m, box("mouth_open")]) : m
3464
+ };
3465
+ }
3466
+
3231
3467
  // src/auto-rig/generate.ts
3232
3468
  var import_format10 = require("@ikijs/format");
3233
3469
 
3234
3470
  // src/auto-rig/animations.ts
3235
- var import_format7 = require("@ikijs/format");
3471
+ var import_format8 = require("@ikijs/format");
3236
3472
  function browsY(y) {
3237
3473
  return [
3238
- { parameter: import_format7.StandardParameter.BrowLeftY, value: y, blend: "add" },
3239
- { parameter: import_format7.StandardParameter.BrowRightY, value: y, blend: "add" }
3474
+ { parameter: import_format8.StandardParameter.BrowLeftY, value: y, blend: "add" },
3475
+ { parameter: import_format8.StandardParameter.BrowRightY, value: y, blend: "add" }
3240
3476
  ];
3241
3477
  }
3242
3478
  function browsTilt(innerDown) {
3243
3479
  return [
3244
- { parameter: import_format7.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
3245
- { parameter: import_format7.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
3480
+ { parameter: import_format8.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
3481
+ { parameter: import_format8.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
3246
3482
  ];
3247
3483
  }
3248
3484
  function cheek(value) {
3249
- return { parameter: import_format7.StandardParameter.Cheek, value, blend: "add" };
3485
+ return { parameter: import_format8.StandardParameter.Cheek, value, blend: "add" };
3250
3486
  }
3251
- var BROWS = [import_format7.StandardParameter.BrowLeftY, import_format7.StandardParameter.BrowRightY, import_format7.StandardParameter.BrowLeftAngle, import_format7.StandardParameter.BrowRightAngle];
3487
+ var BROWS = [import_format8.StandardParameter.BrowLeftY, import_format8.StandardParameter.BrowRightY, import_format8.StandardParameter.BrowLeftAngle, import_format8.StandardParameter.BrowRightAngle];
3252
3488
  var EXPRESSIONS = [
3253
3489
  {
3254
3490
  id: "smile",
3255
3491
  description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
3256
3492
  parameters: [
3257
- { parameter: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
3493
+ { parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
3258
3494
  ...browsY(0.25),
3259
3495
  cheek(0.4)
3260
3496
  ]
@@ -3263,10 +3499,10 @@ var EXPRESSIONS = [
3263
3499
  id: "laugh",
3264
3500
  description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
3265
3501
  parameters: [
3266
- { parameter: import_format7.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
3267
- { parameter: import_format7.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
3268
- { parameter: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
3269
- { parameter: import_format7.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
3502
+ { parameter: import_format8.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
3503
+ { parameter: import_format8.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
3504
+ { parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
3505
+ { parameter: import_format8.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
3270
3506
  ...browsY(0.4),
3271
3507
  cheek(0.65)
3272
3508
  ]
@@ -3278,7 +3514,7 @@ var EXPRESSIONS = [
3278
3514
  parameters: [
3279
3515
  ...browsY(-0.5),
3280
3516
  ...browsTilt(0.8),
3281
- { parameter: import_format7.StandardParameter.MouthForm, value: -0.9, blend: "add" }
3517
+ { parameter: import_format8.StandardParameter.MouthForm, value: -0.9, blend: "add" }
3282
3518
  ]
3283
3519
  },
3284
3520
  {
@@ -3288,7 +3524,7 @@ var EXPRESSIONS = [
3288
3524
  parameters: [
3289
3525
  ...browsY(0.35),
3290
3526
  ...browsTilt(-0.8),
3291
- { parameter: import_format7.StandardParameter.MouthForm, value: -0.8, blend: "add" }
3527
+ { parameter: import_format8.StandardParameter.MouthForm, value: -0.8, blend: "add" }
3292
3528
  ]
3293
3529
  },
3294
3530
  {
@@ -3296,18 +3532,18 @@ var EXPRESSIONS = [
3296
3532
  description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
3297
3533
  parameters: [
3298
3534
  ...browsY(0.8),
3299
- { parameter: import_format7.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
3535
+ { parameter: import_format8.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
3300
3536
  ]
3301
3537
  },
3302
3538
  {
3303
3539
  id: "shy",
3304
3540
  description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
3305
- needs: [import_format7.StandardParameter.Cheek],
3541
+ needs: [import_format8.StandardParameter.Cheek],
3306
3542
  parameters: [
3307
3543
  cheek(1),
3308
3544
  // Gaze down.
3309
- { parameter: import_format7.StandardParameter.EyeballY, value: -0.65, blend: "add" },
3310
- { parameter: import_format7.StandardParameter.MouthForm, value: 0.4, blend: "add" },
3545
+ { parameter: import_format8.StandardParameter.EyeballY, value: -0.65, blend: "add" },
3546
+ { parameter: import_format8.StandardParameter.MouthForm, value: 0.4, blend: "add" },
3311
3547
  ...browsTilt(-0.25)
3312
3548
  ]
3313
3549
  }
@@ -3327,7 +3563,7 @@ var HEAD_MOTIONS = {
3327
3563
  duration: 1,
3328
3564
  curves: [
3329
3565
  {
3330
- parameter: import_format7.StandardParameter.AngleY,
3566
+ parameter: import_format8.StandardParameter.AngleY,
3331
3567
  keys: [
3332
3568
  [0, 0],
3333
3569
  [0.3, -18],
@@ -3348,7 +3584,7 @@ var HEAD_MOTIONS = {
3348
3584
  fadeIn: 0.15,
3349
3585
  curves: [
3350
3586
  {
3351
- parameter: import_format7.StandardParameter.AngleX,
3587
+ parameter: import_format8.StandardParameter.AngleX,
3352
3588
  keys: [
3353
3589
  [0, 0],
3354
3590
  [0.2, -18],
@@ -3367,7 +3603,7 @@ var HEAD_MOTIONS = {
3367
3603
  duration: 1.4,
3368
3604
  curves: [
3369
3605
  {
3370
- parameter: import_format7.StandardParameter.AngleZ,
3606
+ parameter: import_format8.StandardParameter.AngleZ,
3371
3607
  keys: [
3372
3608
  [0, 0],
3373
3609
  [0.35, 20],
@@ -3388,7 +3624,7 @@ var WAVE = {
3388
3624
  fadeOut: 0.15,
3389
3625
  curves: [
3390
3626
  {
3391
- parameter: import_format7.StandardParameter.ArmPoseRight,
3627
+ parameter: import_format8.StandardParameter.ArmPoseRight,
3392
3628
  interpolation: "linear",
3393
3629
  keys: [
3394
3630
  [0, 0],
@@ -3398,7 +3634,7 @@ var WAVE = {
3398
3634
  ]
3399
3635
  },
3400
3636
  {
3401
- parameter: import_format7.StandardParameter.ArmRight,
3637
+ parameter: import_format8.StandardParameter.ArmRight,
3402
3638
  keys: [
3403
3639
  [0, 0],
3404
3640
  [0.4, 10],
@@ -3407,7 +3643,7 @@ var WAVE = {
3407
3643
  ]
3408
3644
  },
3409
3645
  {
3410
- parameter: import_format7.StandardParameter.ArmPoseAngleRight,
3646
+ parameter: import_format8.StandardParameter.ArmPoseAngleRight,
3411
3647
  keys: [
3412
3648
  [0, 0],
3413
3649
  [0.5, 0],
@@ -3423,7 +3659,7 @@ var WAVE = {
3423
3659
  ]
3424
3660
  };
3425
3661
  function defaultMotions(declared) {
3426
- const waves = [import_format7.StandardParameter.ArmRight, import_format7.StandardParameter.ArmPoseRight, import_format7.StandardParameter.ArmPoseAngleRight].every(
3662
+ const waves = [import_format8.StandardParameter.ArmRight, import_format8.StandardParameter.ArmPoseRight, import_format8.StandardParameter.ArmPoseAngleRight].every(
3427
3663
  (id) => declared.has(id)
3428
3664
  );
3429
3665
  return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
@@ -3661,179 +3897,6 @@ function checkStrandEdges(strand, irises, hair) {
3661
3897
  // src/auto-rig/context.ts
3662
3898
  var import_format9 = require("@ikijs/format");
3663
3899
 
3664
- // src/auto-rig/mouth.ts
3665
- var import_format8 = require("@ikijs/format");
3666
- var UPPER_SHARE = 0.3;
3667
- var MOUTH_KNOT_PX = 4;
3668
- var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
3669
- function columnRuns(layer) {
3670
- const cols = /* @__PURE__ */ new Map();
3671
- (layer.rowRuns ?? []).forEach((runs, k) => {
3672
- for (let i = 0; i < runs.length; i += 2) {
3673
- for (let c = runs[i]; c < runs[i + 1]; c++) {
3674
- const list = cols.get(c) ?? [];
3675
- const last = list[list.length - 1];
3676
- if (last !== void 0 && last[1] === k) last[1] = k + 1;
3677
- else list.push([k, k + 1]);
3678
- cols.set(c, list);
3679
- }
3680
- }
3681
- });
3682
- return cols;
3683
- }
3684
- var edgesOf = (layer, run) => ({
3685
- top: layer.canvasH / 2 - (layer.bbox.y + run[0]),
3686
- bottom: layer.canvasH / 2 - (layer.bbox.y + run[1])
3687
- });
3688
- function mouthOpening(byRole) {
3689
- const inner = byRole.get("mouth_inner");
3690
- const upper = byRole.get("lip_upper");
3691
- const lower = byRole.get("lip_lower");
3692
- const innerCols = columnRuns(inner);
3693
- if (innerCols.size === 0) {
3694
- throw new LayerGeometryError(
3695
- `auto-rig: layer "${inner.fileName}": mouth_inner has no opaque pixel (alpha \u2265 128) to read the opening off`
3696
- );
3697
- }
3698
- const upperCols = columnRuns(upper);
3699
- const lowerCols = columnRuns(lower);
3700
- const cols = [...innerCols.keys()];
3701
- const x0 = Math.min(...cols);
3702
- const x1 = Math.max(...cols);
3703
- const read = (col) => {
3704
- const runs = innerCols.get(col);
3705
- if (runs === void 0) return void 0;
3706
- const T = edgesOf(inner, runs[0]).top;
3707
- const Bb = edgesOf(inner, runs[runs.length - 1]).bottom;
3708
- const line = [...upperCols.get(col) ?? []].reverse().map((r) => edgesOf(upper, r)).find((e) => e.top >= T);
3709
- const Tu = line?.bottom ?? T;
3710
- const lineTop = line?.top ?? T;
3711
- const skin = lowerCols.get(col)?.[0];
3712
- return {
3713
- T,
3714
- Bb,
3715
- Tu,
3716
- lineTop,
3717
- lineH: lineTop - Tu,
3718
- Bl: skin === void 0 ? Bb : edgesOf(lower, skin).top,
3719
- H: T - Bb,
3720
- seam: T - UPPER_SHARE * (T - Bb)
3721
- };
3722
- };
3723
- const reads = [];
3724
- for (let c = x0; c <= x1; c++) {
3725
- let r;
3726
- for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
3727
- reads.push(r);
3728
- }
3729
- const base = (col) => reads[Math.min(x1, Math.max(x0, col)) - x0];
3730
- const centre = Math.round((x0 + x1) / 2);
3731
- const w = base(centre).lineH;
3732
- return {
3733
- x0,
3734
- x1,
3735
- centre,
3736
- canvasW: inner.canvasW,
3737
- w,
3738
- at(col) {
3739
- const b = base(col);
3740
- return { ...b, overlap: Math.max(0, Math.min(w, b.lineH)) };
3741
- }
3742
- };
3743
- }
3744
- function mouthFold(role, opening) {
3745
- return (col, y, v) => {
3746
- if (v === 1) return 0;
3747
- if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
3748
- return 0;
3749
- const c = opening.at(col);
3750
- const k = 1 - v;
3751
- switch (role) {
3752
- case "lip_upper":
3753
- return (c.seam - c.Tu) * k;
3754
- case "lip_lower":
3755
- return (c.seam + c.overlap - c.Bl) * k;
3756
- case "mouth_inner":
3757
- return (c.seam + c.overlap - y) * k;
3758
- }
3759
- };
3760
- }
3761
- function columnOfKnot(knot, opening, role) {
3762
- const boundary = Math.round(knot + opening.canvasW / 2);
3763
- const col = boundary === opening.x1 + 1 ? opening.x1 : boundary;
3764
- if (col >= opening.x0 && col <= opening.x1) return col;
3765
- return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
3766
- }
3767
- function mouthFoldWarp(role, mesh, xs, box, opening) {
3768
- const fold = mouthFold(role, opening);
3769
- return localWarp(import_format8.StandardParameter.MouthOpen, mesh, box, [0, 1], (p, v, i) => {
3770
- const col = columnOfKnot(xs[i % xs.length], opening, role);
3771
- return [0, col === void 0 ? 0 : fold(col, p[1], v)];
3772
- });
3773
- }
3774
- function mouthKnots(union, opening) {
3775
- const half = opening.canvasW / 2;
3776
- const step = MOUTH_KNOT_PX;
3777
- const xs = [union.x0];
3778
- while (xs[xs.length - 1] + step < union.x1 - 0.3 * step)
3779
- xs.push(xs[xs.length - 1] + step);
3780
- xs.push(union.x1);
3781
- const fixed = /* @__PURE__ */ new Set([union.x0, union.x1]);
3782
- for (const x of [opening.x0 - half, opening.x1 + 1 - half]) {
3783
- fixed.add(x);
3784
- if (xs.includes(x)) continue;
3785
- const near = xs.findIndex((v) => Math.abs(v - x) < 0.3 * step);
3786
- if (near >= 0 && !fixed.has(xs[near])) xs[near] = x;
3787
- else xs.push(x);
3788
- }
3789
- xs.sort((p, q) => p - q);
3790
- for (const x of xs) {
3791
- if (!Number.isInteger(x + half)) {
3792
- throw new Error(
3793
- `auto-rig: mouth knot ${x} is not an integer canvas boundary`
3794
- );
3795
- }
3796
- }
3797
- return xs;
3798
- }
3799
- function mouthMesh(box, knots) {
3800
- const xs = [box.x0, ...knots.filter((k) => k > box.x0 && k < box.x1), box.x1];
3801
- const rows = cellsFor(bh(box), MOUTH_KNOT_PX, 2, 40);
3802
- const ys = Array.from(
3803
- { length: rows + 1 },
3804
- (_, r) => box.y1 - bh(box) * r / rows
3805
- );
3806
- return { mesh: columnMesh(box, { xs, ys }), xs };
3807
- }
3808
- function buildMouthRig(byRole) {
3809
- const opening = mouthOpening(byRole);
3810
- const union = unionBoxes(LIP_ROLES.map((r) => boxOfLayer(byRole.get(r))));
3811
- return {
3812
- opening,
3813
- frame: mouthFrameOf(union),
3814
- knots: mouthKnots(union, opening),
3815
- union
3816
- };
3817
- }
3818
- function mouthAnchor(byRole) {
3819
- const box = (role) => boxOfLayer(byRole.get(role));
3820
- if (isLipSet(byRole)) {
3821
- const opening = mouthOpening(byRole);
3822
- return {
3823
- at: {
3824
- x: cx(box("mouth_inner")),
3825
- y: opening.at(opening.centre).seam
3826
- },
3827
- box: unionBoxes(LIP_ROLES.map(box))
3828
- };
3829
- }
3830
- const m = box("mouth");
3831
- return {
3832
- at: { x: cx(m), y: cy(m) },
3833
- box: byRole.has("mouth_open") ? unionBoxes([m, box("mouth_open")]) : m
3834
- };
3835
- }
3836
-
3837
3900
  // src/auto-rig/head.ts
3838
3901
  function outerEdge(runs, axisX, side) {
3839
3902
  if (runs.length === 0) return void 0;
@@ -6246,6 +6309,7 @@ function declareParameters(roles) {
6246
6309
  DeletePhysicsRig,
6247
6310
  EditorDocument,
6248
6311
  HEAD_BAND,
6312
+ LIP_ROLES,
6249
6313
  LayerGeometryError,
6250
6314
  SPECK_CORE_FRACTION,
6251
6315
  SetDeformerBindings,
@@ -6267,6 +6331,7 @@ function declareParameters(roles) {
6267
6331
  UV_INSET_PX,
6268
6332
  armGeometry,
6269
6333
  captureBindingEndpoint,
6334
+ columnRuns,
6270
6335
  computeGridOffsets,
6271
6336
  createDefaultMatrixDeformer,
6272
6337
  createDefaultPart,
@@ -6281,6 +6346,9 @@ function declareParameters(roles) {
6281
6346
  headHalfOf,
6282
6347
  interpolateGridOffsets,
6283
6348
  isSpeckCore,
6349
+ mouthFold,
6350
+ mouthOpening,
6351
+ mouthRestShift,
6284
6352
  packAtlas,
6285
6353
  parseLayerRoles,
6286
6354
  partIdsOfRole,