@ikijs/editor 0.8.0 → 0.8.1

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.mjs CHANGED
@@ -1991,6 +1991,7 @@ function meshCellsFor(w, h) {
1991
1991
  }
1992
1992
  var FACE_PLATE_CELLS = 10;
1993
1993
  var FEATURE_GRID_CELLS = 4;
1994
+ var MOUTH_GRID_CELL_PX = 32;
1994
1995
  var TURN_LATTICE_CELL_PX = 16;
1995
1996
  var GRID_MARGIN = 0.12;
1996
1997
  var HEAD_CYLINDER_RADIUS_FACTOR = 0.6 / 0.5;
@@ -2141,31 +2142,33 @@ function turnColumnMap(grid, faceCenterX, radiusX, angleX, travel) {
2141
2142
  x + boundedCylinderBend(x - faceCenterX, radiusX, theta) + slide
2142
2143
  );
2143
2144
  }
2144
- const cellFor = (v, edges) => {
2145
+ const cellFor = (x) => {
2145
2146
  let lo = 0;
2146
2147
  let hi = grid.cols - 1;
2147
2148
  while (lo < hi) {
2148
2149
  const mid = lo + hi >> 1;
2149
- if (v < edges[mid + 1]) hi = mid;
2150
+ if (x < restX[mid + 1]) hi = mid;
2150
2151
  else lo = mid + 1;
2151
2152
  }
2152
2153
  return lo;
2153
2154
  };
2154
- const sample = (edges, values, v) => {
2155
- const c = cellFor(v, edges);
2156
- const s = Math.max(
2157
- 0,
2158
- Math.min(1, (v - edges[c]) / (edges[c + 1] - edges[c]))
2159
- );
2160
- if (s === 0) return values[c];
2161
- if (s === 1) return values[c + 1];
2162
- return values[c] + (values[c + 1] - values[c]) * s;
2163
- };
2164
2155
  return {
2165
2156
  restX,
2166
2157
  warpedX,
2167
- mapX: (x) => sample(restX, warpedX, x),
2168
- invertX: (X) => sample(warpedX, restX, X)
2158
+ // Piecewise-linear read of `warpedX` over `restX`, the within-cell
2159
+ // fraction clamped to [0,1] as sampleWarpGrid clamps (s, t). A node reads
2160
+ // its own warped x: `warpedX[c] + Δ·1` is `warpedX[c + 1]` only to an
2161
+ // ulp, and the columns a bake reads are nodes.
2162
+ mapX: (x) => {
2163
+ const c = cellFor(x);
2164
+ const s = Math.max(
2165
+ 0,
2166
+ Math.min(1, (x - restX[c]) / (restX[c + 1] - restX[c]))
2167
+ );
2168
+ if (s === 0) return warpedX[c];
2169
+ if (s === 1) return warpedX[c + 1];
2170
+ return warpedX[c] + (warpedX[c + 1] - warpedX[c]) * s;
2171
+ }
2169
2172
  };
2170
2173
  }
2171
2174
  function renderedLandingX(part, vertexLanding, px, py) {
@@ -2271,7 +2274,7 @@ function plateLandingOn(plate, mapAt) {
2271
2274
  y
2272
2275
  );
2273
2276
  }
2274
- function plateGuardRowsFor(face, hairFront) {
2277
+ function plateGuardRowsFor(face, hairFront, widestY = -Infinity) {
2275
2278
  const rowsOf = (part, y) => {
2276
2279
  const { rows } = meshCellsFor(part.cropW, part.cropH);
2277
2280
  return Array.from(
@@ -2286,7 +2289,7 @@ function plateGuardRowsFor(face, hairFront) {
2286
2289
  return [
2287
2290
  ...faceRows,
2288
2291
  ...rowsOf(hairFront, hairFront.centerY).map(
2289
- (y) => Math.max(bottom, Math.min(top, y))
2292
+ (y) => Math.max(bottom, Math.min(top, Math.max(y, widestY)))
2290
2293
  )
2291
2294
  ];
2292
2295
  }
@@ -2298,7 +2301,7 @@ function plateGuardsOf(carriers, faceHalfWidth, profile, hairFront) {
2298
2301
  return {
2299
2302
  plate,
2300
2303
  edgeAt: profile === void 0 ? () => faceHalfWidth : profile.at,
2301
- plateGuardRows: plateGuardRowsFor(plate.part, hairFront)
2304
+ plateGuardRows: plateGuardRowsFor(plate.part, hairFront, profile?.widestY)
2302
2305
  };
2303
2306
  }
2304
2307
  var TurnTargetError = class extends Error {
@@ -2476,14 +2479,15 @@ var TURN_SWEEP_SAMPLES = 32;
2476
2479
  var HOLD_CLEARANCE = 1;
2477
2480
  var TURN_HOLD_SAMPLES = 16;
2478
2481
  var TURN_SETTLE_PASSES = 24;
2479
- function hairFrontHoldTarget(x, y, deg, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdge) {
2482
+ function hairFrontHoldTarget(x, y, deg, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdge, widestY = -Infinity) {
2480
2483
  const local = x - faceCenterX;
2481
2484
  const dist = Math.abs(local);
2482
2485
  const side = Math.sign(local);
2483
- const painted = edgeAt(y);
2484
- if (dist <= painted) return plateLandingAt(deg, x, y);
2486
+ const row = Math.max(y, widestY);
2487
+ const painted = edgeAt(row);
2488
+ if (dist <= painted) return plateLandingAt(deg, x, row);
2485
2489
  if (dist <= holdBase) {
2486
- const inner = plateLandingAt(deg, faceCenterX + side * painted, y);
2490
+ const inner = plateLandingAt(deg, faceCenterX + side * painted, row);
2487
2491
  const u = (dist - painted) / (holdBase - painted);
2488
2492
  return inner + (faceCenterX + side * holdEdge - inner) * u;
2489
2493
  }
@@ -2588,7 +2592,8 @@ function evaluateTurnCandidate(ctx, radius) {
2588
2592
  plateLandingAt,
2589
2593
  ctx.edgeAt,
2590
2594
  hold.holdBase,
2591
- hold.holdEdgeAt(deg)
2595
+ hold.holdEdgeAt(deg),
2596
+ ctx.profile?.widestY
2592
2597
  ),
2593
2598
  leadShift
2594
2599
  );
@@ -3563,7 +3568,7 @@ function plateReachAt(plateLandingAt, faceCenterX, edgeAt, plateGuardRows, deg)
3563
3568
  )
3564
3569
  );
3565
3570
  }
3566
- function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdgeAt, plateGuardRows) {
3571
+ function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandingAt, edgeAt, holdBase, holdEdgeAt, plateGuardRows, widestY = -Infinity) {
3567
3572
  if (holdEdgeAt(0) !== holdBase) {
3568
3573
  throw new Error(
3569
3574
  `auto-rig: bakeHairFrontSilhouetteWarp: holdEdgeAt(0) is ${holdEdgeAt(0)}, not the hold edge's own rest distance ${holdBase}, so the bangs would move in the rest pose`
@@ -3601,7 +3606,8 @@ function bakeHairFrontSilhouetteWarp(mesh, partX, partY, faceCenterX, plateLandi
3601
3606
  plateLandingAt,
3602
3607
  edgeAt,
3603
3608
  holdBase,
3604
- holdEdge
3609
+ holdEdge,
3610
+ widestY
3605
3611
  ) - x,
3606
3612
  0
3607
3613
  );
@@ -3780,7 +3786,7 @@ function defaultPoseOf(bindings) {
3780
3786
  return binding.from + (binding.to - binding.from) * t;
3781
3787
  });
3782
3788
  }
3783
- function groupGridFor(members, cells, turnShiftPx, nodShiftPx) {
3789
+ function groupGridFor(members, cells, turnShiftPx, nodShiftPx, cellPx) {
3784
3790
  let minX = Infinity;
3785
3791
  let maxX = -Infinity;
3786
3792
  let minY = Infinity;
@@ -3815,24 +3821,50 @@ function groupGridFor(members, cells, turnShiftPx, nodShiftPx) {
3815
3821
  }
3816
3822
  }
3817
3823
  }
3824
+ const extentMinX = minX;
3825
+ const extentMaxX = maxX;
3818
3826
  minX -= turnShiftPx;
3819
3827
  maxX += turnShiftPx;
3820
3828
  minY -= nodShiftPx;
3821
3829
  maxY += nodShiftPx;
3822
3830
  const marginX = (maxX - minX) * GRID_MARGIN + 1;
3823
3831
  const marginY = (maxY - minY) * GRID_MARGIN + 1;
3824
- return {
3825
- cols: cells,
3826
- rows: cells,
3827
- points: generateGridPoints(
3828
- cells,
3829
- cells,
3830
- minX - marginX,
3831
- maxX + marginX,
3832
- minY - marginY,
3833
- maxY + marginY
3834
- )
3835
- };
3832
+ const points = generateGridPoints(
3833
+ cells,
3834
+ cells,
3835
+ minX - marginX,
3836
+ maxX + marginX,
3837
+ minY - marginY,
3838
+ maxY + marginY
3839
+ );
3840
+ if (cellPx === void 0) return { cols: cells, rows: cells, points };
3841
+ const stride = cells + 1;
3842
+ const uniformXs = Array.from({ length: stride }, (_, c) => points[c * 2]);
3843
+ const ys = Array.from(
3844
+ { length: stride },
3845
+ (_, r) => points[r * stride * 2 + 1]
3846
+ );
3847
+ const xs = [uniformXs[0]];
3848
+ for (let c = 0; c < cells; c++) {
3849
+ const left = uniformXs[c];
3850
+ const right = uniformXs[c + 1];
3851
+ const from = Math.max(left, extentMinX);
3852
+ const to = Math.min(right, extentMaxX);
3853
+ if (right - left > cellPx && from < to) {
3854
+ const steps = Math.ceil((to - from) / cellPx);
3855
+ if (from > left) xs.push(from);
3856
+ for (let k = 1; k < steps; k++) {
3857
+ xs.push(from + k * (to - from) / steps);
3858
+ }
3859
+ if (to < right) xs.push(to);
3860
+ }
3861
+ xs.push(right);
3862
+ }
3863
+ const merged = [];
3864
+ for (const y of ys) {
3865
+ for (const x of xs) merged.push(x, y);
3866
+ }
3867
+ return { cols: xs.length - 1, rows: cells, points: merged };
3836
3868
  }
3837
3869
  function eyelidFoldFor(layers, layer, mesh, t) {
3838
3870
  const side = ROLE_TABLE[layer.role].eyeSide;
@@ -3933,7 +3965,8 @@ function turnSetup(layers) {
3933
3965
  own,
3934
3966
  isPlate ? FACE_PLATE_CELLS : FEATURE_GRID_CELLS,
3935
3967
  isPlate ? 0 : turnShift[turnFamily(role)],
3936
- nodShiftPx
3968
+ nodShiftPx,
3969
+ group === "mouthWarp" ? MOUTH_GRID_CELL_PX : void 0
3937
3970
  )
3938
3971
  );
3939
3972
  }
@@ -4273,7 +4306,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
4273
4306
  edgeAt,
4274
4307
  holdBase,
4275
4308
  turn?.holdEdgeAt ?? (() => holdBase),
4276
- plateGuardRows
4309
+ plateGuardRows,
4310
+ profile?.widestY
4277
4311
  )
4278
4312
  );
4279
4313
  ownWarps.push(