@ikijs/editor 0.18.0 → 0.19.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/README.md CHANGED
@@ -82,9 +82,9 @@ it), the ears lag the face, the far one narrowing. AngleZ rolls the head 14° ab
82
82
  A `body` rides a body warp that breathes, follows the head's turn and tilt a
83
83
  little and turns on `ParamBodyAngleX/Y/Z`, its legs planted, and the head
84
84
  hangs from it. `arm_L` / `arm_R`, each a whole arm drawn hanging with its
85
- shoulder at the top, hang from it too: each becomes an upper arm and a
86
- forearm cut from the one crop, turning about the shoulder and the elbow on
87
- `ParamArmL/R` and `ParamElbowL/R`. An arm needs a `body`.
85
+ shoulder at the top, hang from it too: each becomes an elbow cap, an upper
86
+ arm and a forearm cut from the one crop, turning about the shoulder and the
87
+ elbow on `ParamArmL/R` and `ParamElbowL/R`. An arm needs a `body`.
88
88
 
89
89
  On a layer set with a `nose`, `options.turnTargets` — the cues a 30° reference
90
90
  measures (how far the eye pair slides, how much the far eye foreshortens,
package/dist/index.d.mts CHANGED
@@ -857,31 +857,43 @@ interface GenerateOptions {
857
857
 
858
858
  /**
859
859
  * The arms. Each `arm_L` / `arm_R` layer is one drawing of the whole arm,
860
- * hanging, its shoulder at the top. It becomes two parts on two nested matrix
861
- * deformers hung from the body warp:
860
+ * hanging, its shoulder at the top. It becomes three parts on two nested
861
+ * matrix deformers hung from the body warp:
862
862
  * - the upper arm (the role's own id) on `armDeformer_X`, which turns about
863
863
  * the shoulder, the deltoid cap's centre;
864
864
  * - the forearm (`forearm_X`) on `forearmDeformer_X`, a child of the arm's,
865
- * which turns about the elbow.
865
+ * which turns about the elbow;
866
+ * - the elbow cap (`elbow_X`), also on `forearmDeformer_X`, drawn behind the
867
+ * upper arm.
866
868
  * The shoulder pivot rides an all-weight-1 cell of the body warp (`body.ts`),
867
869
  * so the arm breathes and turns with the chest rigidly, never sheared.
868
870
  *
869
- * Both parts are cut from the one crop: they share its box, transform and
870
- * texture rect, and their meshes meet on the seam, the elbow pivot's row. The
871
- * upper arm is the band above the seam. The forearm is the band below it plus
872
- * a half-disc fan above it, centred on the elbow pivot. Iki has no glue
873
- * between meshes, and two bands hinged at a seam open a wedge as wide as the
874
- * arm on a bend; the half-disc turns about its own centre, so it rotates into
875
- * itself and keeps the joint covered at any bend.
871
+ * All three parts are cut from the one crop: they share its box, transform
872
+ * and texture rect. The upper arm is the band above the seam, the elbow
873
+ * pivot's row; the forearm is the band below it. Iki has no glue between
874
+ * meshes, and two bands hinged at a seam open a wedge as wide as the arm on a
875
+ * bend. The cap fills it: a half-disc fan centred on the elbow pivot turns
876
+ * about its own centre, so it rotates into itself and keeps the joint covered
877
+ * at any bend.
876
878
  *
877
- * Draw order: hair_back < body < arm_L, forearm_L, arm_R, forearm_R < face …
878
- * < hair_front. The forearm draws over the upper arm, so its cap hides the
879
- * seam.
879
+ * Behind the upper arm, the cap shows only where the forearm's bend uncovers
880
+ * it, so it must lie inside the paint at rest, or its edge would show past
881
+ * the arm's. Its boundary is therefore the arm's own contour, not a circle:
882
+ * `armGeometry` walks each of the fan's 13 rays out from the elbow to the
883
+ * contour (`capRays`). A ring at the boundary, `CAP_RIM` wide, takes the UVs
884
+ * of the seam row's edge cross-section swept round the arc, so the exposed
885
+ * wedge carries the painted line, fading out in the antialias column as the
886
+ * painted edge does; the fan inside it is the crop's own pixels.
880
887
  *
881
- * Signs: ParamArmX + raises the arm outward on either side and ParamElbowX +
882
- * turns the forearm the same way, both in degrees 1:1. A rotation is
883
- * CCW-positive, so an arm whose shoulder lies right of the body's axis (+x,
884
- * the character's left) turns CCW for +, and one at −x turns CW.
888
+ * Draw order: hair_back < body < elbow_L, arm_L, forearm_L, elbow_R, arm_R,
889
+ * forearm_R < face … < hair_front. The forearm draws over the upper arm,
890
+ * which draws over the cap.
891
+ *
892
+ * Signs: ParamArmX + raises the arm outward on either side, in degrees 1:1.
893
+ * A rotation is CCW-positive, so an arm whose shoulder lies right of the
894
+ * body's axis (+x, the character's left) turns CCW for +, and one at −x turns
895
+ * CW. ParamElbowX + bends the forearm toward the body, the opposite turn to
896
+ * the shoulder's +: it turns CW on the +x side, CCW on the −x side.
885
897
  */
886
898
 
887
899
  type Point = {
@@ -895,8 +907,14 @@ interface ArmGeometry {
895
907
  shoulderRadius: number;
896
908
  /** The elbow pivot, model space. Its y is the seam the two meshes meet on. */
897
909
  elbow: Point;
898
- /** The forearm's half-disc radius, px. */
910
+ /** The elbow cap's largest radius, px. */
899
911
  capRadius: number;
912
+ /** The cap's radius on each of its `CAP_SEGMENTS + 1` rays, from the elbow
913
+ * at angles 0..π, px: the walk out to the contour, less `capInset`, at
914
+ * most `capRadius`. */
915
+ capRays: number[];
916
+ /** The seam row's widest run, crop columns [start, end). */
917
+ seamRun: [number, number];
900
918
  /** +1 for a shoulder right of the body's axis (+ turns CCW), −1 left of it
901
919
  * (+ turns CW). */
902
920
  side: 1 | -1;
@@ -910,7 +928,12 @@ interface ArmGeometry {
910
928
  * run centre of the row it lands in;
911
929
  * - the elbow is the centre of the row `ELBOW_AT` of the way from there to
912
930
  * the last painted row, at its run centre;
913
- * - the cap radius is half the elbow's run + 1 px, kept inside the crop.
931
+ * - the cap radius is half the elbow's run + 1 px, kept inside the crop;
932
+ * - each cap ray walks 1 px at a time from the elbow until it leaves the
933
+ * widest run of the crop row it is in (strictly inside: `start < x < end`,
934
+ * on canvas columns, the row the floor of the canvas y), then stops
935
+ * `capInset(i)` short of that exit (none on the two seam-end rays, 2 px on
936
+ * the next, `CAP_INSET` on the rest), and at `capRadius`.
914
937
  * Pivots are on the 0.01 grid, as the written model is. `bodyAxisX` is the
915
938
  * body's axis, model x: the shoulder's side of it sets the sign. An arm too
916
939
  * short for its width to put the elbow under the shoulder would rig
@@ -938,8 +961,8 @@ declare function parseLayerRoles(fileNames: string[]): {
938
961
  * (`body.ts`) that breathes, follows the head's turn and tilt a little and
939
962
  * turns on BodyAngleX/Y/Z, its legs planted; a head deformer hung from it
940
963
  * that rolls about the chin and breathes; each arm hung from it too, as an
941
- * upper arm and a forearm turning about the shoulder and the elbow
942
- * (`arms.ts`); one small warp grid per feature
964
+ * upper arm, a forearm and an elbow cap turning about the shoulder and the
965
+ * elbow (`arms.ts`); one small warp grid per feature
943
966
  * (each eye, each brow, the nose, the mouth), translating and foreshortening
944
967
  * it by its own lead over the plate; and the plate, the hair and the blush
945
968
  * moved by per-vertex keyforms of their own — the plate as up to four
package/dist/index.d.ts CHANGED
@@ -857,31 +857,43 @@ interface GenerateOptions {
857
857
 
858
858
  /**
859
859
  * The arms. Each `arm_L` / `arm_R` layer is one drawing of the whole arm,
860
- * hanging, its shoulder at the top. It becomes two parts on two nested matrix
861
- * deformers hung from the body warp:
860
+ * hanging, its shoulder at the top. It becomes three parts on two nested
861
+ * matrix deformers hung from the body warp:
862
862
  * - the upper arm (the role's own id) on `armDeformer_X`, which turns about
863
863
  * the shoulder, the deltoid cap's centre;
864
864
  * - the forearm (`forearm_X`) on `forearmDeformer_X`, a child of the arm's,
865
- * which turns about the elbow.
865
+ * which turns about the elbow;
866
+ * - the elbow cap (`elbow_X`), also on `forearmDeformer_X`, drawn behind the
867
+ * upper arm.
866
868
  * The shoulder pivot rides an all-weight-1 cell of the body warp (`body.ts`),
867
869
  * so the arm breathes and turns with the chest rigidly, never sheared.
868
870
  *
869
- * Both parts are cut from the one crop: they share its box, transform and
870
- * texture rect, and their meshes meet on the seam, the elbow pivot's row. The
871
- * upper arm is the band above the seam. The forearm is the band below it plus
872
- * a half-disc fan above it, centred on the elbow pivot. Iki has no glue
873
- * between meshes, and two bands hinged at a seam open a wedge as wide as the
874
- * arm on a bend; the half-disc turns about its own centre, so it rotates into
875
- * itself and keeps the joint covered at any bend.
871
+ * All three parts are cut from the one crop: they share its box, transform
872
+ * and texture rect. The upper arm is the band above the seam, the elbow
873
+ * pivot's row; the forearm is the band below it. Iki has no glue between
874
+ * meshes, and two bands hinged at a seam open a wedge as wide as the arm on a
875
+ * bend. The cap fills it: a half-disc fan centred on the elbow pivot turns
876
+ * about its own centre, so it rotates into itself and keeps the joint covered
877
+ * at any bend.
876
878
  *
877
- * Draw order: hair_back < body < arm_L, forearm_L, arm_R, forearm_R < face …
878
- * < hair_front. The forearm draws over the upper arm, so its cap hides the
879
- * seam.
879
+ * Behind the upper arm, the cap shows only where the forearm's bend uncovers
880
+ * it, so it must lie inside the paint at rest, or its edge would show past
881
+ * the arm's. Its boundary is therefore the arm's own contour, not a circle:
882
+ * `armGeometry` walks each of the fan's 13 rays out from the elbow to the
883
+ * contour (`capRays`). A ring at the boundary, `CAP_RIM` wide, takes the UVs
884
+ * of the seam row's edge cross-section swept round the arc, so the exposed
885
+ * wedge carries the painted line, fading out in the antialias column as the
886
+ * painted edge does; the fan inside it is the crop's own pixels.
880
887
  *
881
- * Signs: ParamArmX + raises the arm outward on either side and ParamElbowX +
882
- * turns the forearm the same way, both in degrees 1:1. A rotation is
883
- * CCW-positive, so an arm whose shoulder lies right of the body's axis (+x,
884
- * the character's left) turns CCW for +, and one at −x turns CW.
888
+ * Draw order: hair_back < body < elbow_L, arm_L, forearm_L, elbow_R, arm_R,
889
+ * forearm_R < face … < hair_front. The forearm draws over the upper arm,
890
+ * which draws over the cap.
891
+ *
892
+ * Signs: ParamArmX + raises the arm outward on either side, in degrees 1:1.
893
+ * A rotation is CCW-positive, so an arm whose shoulder lies right of the
894
+ * body's axis (+x, the character's left) turns CCW for +, and one at −x turns
895
+ * CW. ParamElbowX + bends the forearm toward the body, the opposite turn to
896
+ * the shoulder's +: it turns CW on the +x side, CCW on the −x side.
885
897
  */
886
898
 
887
899
  type Point = {
@@ -895,8 +907,14 @@ interface ArmGeometry {
895
907
  shoulderRadius: number;
896
908
  /** The elbow pivot, model space. Its y is the seam the two meshes meet on. */
897
909
  elbow: Point;
898
- /** The forearm's half-disc radius, px. */
910
+ /** The elbow cap's largest radius, px. */
899
911
  capRadius: number;
912
+ /** The cap's radius on each of its `CAP_SEGMENTS + 1` rays, from the elbow
913
+ * at angles 0..π, px: the walk out to the contour, less `capInset`, at
914
+ * most `capRadius`. */
915
+ capRays: number[];
916
+ /** The seam row's widest run, crop columns [start, end). */
917
+ seamRun: [number, number];
900
918
  /** +1 for a shoulder right of the body's axis (+ turns CCW), −1 left of it
901
919
  * (+ turns CW). */
902
920
  side: 1 | -1;
@@ -910,7 +928,12 @@ interface ArmGeometry {
910
928
  * run centre of the row it lands in;
911
929
  * - the elbow is the centre of the row `ELBOW_AT` of the way from there to
912
930
  * the last painted row, at its run centre;
913
- * - the cap radius is half the elbow's run + 1 px, kept inside the crop.
931
+ * - the cap radius is half the elbow's run + 1 px, kept inside the crop;
932
+ * - each cap ray walks 1 px at a time from the elbow until it leaves the
933
+ * widest run of the crop row it is in (strictly inside: `start < x < end`,
934
+ * on canvas columns, the row the floor of the canvas y), then stops
935
+ * `capInset(i)` short of that exit (none on the two seam-end rays, 2 px on
936
+ * the next, `CAP_INSET` on the rest), and at `capRadius`.
914
937
  * Pivots are on the 0.01 grid, as the written model is. `bodyAxisX` is the
915
938
  * body's axis, model x: the shoulder's side of it sets the sign. An arm too
916
939
  * short for its width to put the elbow under the shoulder would rig
@@ -938,8 +961,8 @@ declare function parseLayerRoles(fileNames: string[]): {
938
961
  * (`body.ts`) that breathes, follows the head's turn and tilt a little and
939
962
  * turns on BodyAngleX/Y/Z, its legs planted; a head deformer hung from it
940
963
  * that rolls about the chin and breathes; each arm hung from it too, as an
941
- * upper arm and a forearm turning about the shoulder and the elbow
942
- * (`arms.ts`); one small warp grid per feature
964
+ * upper arm, a forearm and an elbow cap turning about the shoulder and the
965
+ * elbow (`arms.ts`); one small warp grid per feature
943
966
  * (each eye, each brow, the nose, the mouth), translating and foreshortening
944
967
  * it by its own lead over the plate; and the plate, the hair and the blush
945
968
  * moved by per-vertex keyforms of their own — the plate as up to four
package/dist/index.js CHANGED
@@ -2754,7 +2754,11 @@ function headOwnBreath(hasBody) {
2754
2754
  var ELBOW_AT = 0.42;
2755
2755
  var CAP_ROWS = [0.1, 0.3];
2756
2756
  var CAP_SEGMENTS = 12;
2757
- var ARM_RANGE = [-30, 150];
2757
+ var CAP_INSET = 3;
2758
+ var capInset = (i) => Math.round(CAP_INSET * Math.min(1, Math.min(i, CAP_SEGMENTS - i) / 2));
2759
+ var CAP_RIM = 0.25;
2760
+ var ARM_RANGE = [-8, 32];
2761
+ var ELBOW_RANGE = [-10, 90];
2758
2762
  var ARM_PARAMS = {
2759
2763
  arm_L: { arm: import_format5.StandardParameter.ArmLeft, elbow: import_format5.StandardParameter.ElbowLeft },
2760
2764
  arm_R: { arm: import_format5.StandardParameter.ArmRight, elbow: import_format5.StandardParameter.ElbowRight }
@@ -2762,11 +2766,13 @@ var ARM_PARAMS = {
2762
2766
  var ARM_IDS = {
2763
2767
  arm_L: {
2764
2768
  forearm: "forearm_L",
2769
+ cap: "elbow_L",
2765
2770
  armDeformer: "armDeformer_L",
2766
2771
  forearmDeformer: "forearmDeformer_L"
2767
2772
  },
2768
2773
  arm_R: {
2769
2774
  forearm: "forearm_R",
2775
+ cap: "elbow_R",
2770
2776
  armDeformer: "armDeformer_R",
2771
2777
  forearmDeformer: "forearmDeformer_R"
2772
2778
  }
@@ -2819,11 +2825,30 @@ function armGeometry(layer, bodyAxisX) {
2819
2825
  b.x1 - elbow.x,
2820
2826
  b.y1 - elbow.y
2821
2827
  );
2828
+ const inside = (x, y) => {
2829
+ const k = Math.floor(y) - bbox.y;
2830
+ if (k < 0 || k >= bbox.h) return false;
2831
+ const [rs, re] = span(k);
2832
+ return rs < x && x < re;
2833
+ };
2834
+ const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
2835
+ const a = Math.PI * i / CAP_SEGMENTS;
2836
+ let t = 1;
2837
+ while (t <= capRadius + CAP_INSET && inside(
2838
+ elbow.x + canvasW / 2 + t * Math.cos(a),
2839
+ canvasH / 2 - elbow.y - t * Math.sin(a)
2840
+ )) {
2841
+ t++;
2842
+ }
2843
+ return Math.min(capRadius, Math.max(1, t - capInset(i)));
2844
+ });
2822
2845
  return {
2823
2846
  shoulder,
2824
2847
  shoulderRadius: ru,
2825
2848
  elbow,
2826
2849
  capRadius,
2850
+ capRays,
2851
+ seamRun: [s - bbox.x, e - bbox.x],
2827
2852
  side: shoulder.x > bodyAxisX ? 1 : -1
2828
2853
  };
2829
2854
  }
@@ -2838,69 +2863,101 @@ function armParts(role, layer, g, order) {
2838
2863
  const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
2839
2864
  const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
2840
2865
  const seam = ly(g.elbow.y);
2841
- const band = (top, bottom) => [
2866
+ const band = (top2, bottom) => [
2842
2867
  -0.5,
2843
- top,
2868
+ top2,
2844
2869
  0.5,
2845
- top,
2870
+ top2,
2846
2871
  -0.5,
2847
2872
  bottom,
2848
2873
  0.5,
2849
2874
  bottom
2850
2875
  ];
2851
2876
  const quad = [2, 3, 0, 0, 3, 1];
2852
- const cap = [lx(g.elbow.x), seam];
2877
+ const rays = g.capRays.length;
2878
+ const top = (rays - 1) / 2;
2879
+ const at = (i, r) => {
2880
+ const a = Math.PI * i / (rays - 1);
2881
+ return [lx(g.elbow.x + r * Math.cos(a)), ly(g.elbow.y + r * Math.sin(a))];
2882
+ };
2883
+ const width = (i) => Math.min(CAP_RIM * g.capRadius, g.capRays[i] / 2);
2884
+ const ringRays = Array.from({ length: rays }, (_, i) => i);
2885
+ ringRays.splice(top, 0, top);
2886
+ const vertices = [lx(g.elbow.x), seam];
2887
+ for (let i = 0; i < rays; i++) {
2888
+ vertices.push(...at(i, g.capRays[i] - width(i)));
2889
+ }
2890
+ const ringInner = vertices.length / 2;
2891
+ for (const i of ringRays) vertices.push(...at(i, g.capRays[i] - width(i)));
2892
+ const ringOuter = vertices.length / 2;
2893
+ for (const i of ringRays) vertices.push(...at(i, g.capRays[i]));
2894
+ const { uvs } = meshOf(vertices, []);
2895
+ const col = (c) => roundTo(Math.min(Math.max(c + 0.5, 0.5), bw(b) - 0.5) / bw(b), 1e-5);
2896
+ const v = roundTo(0.5 - seam, 1e-5);
2897
+ ringRays.forEach((i, j) => {
2898
+ const right = j <= top;
2899
+ const outer = right ? g.seamRun[1] : g.seamRun[0] - 1;
2900
+ const step = right ? -width(i) : width(i);
2901
+ uvs[(ringInner + j) * 2] = col(outer + step);
2902
+ uvs[(ringInner + j) * 2 + 1] = v;
2903
+ uvs[(ringOuter + j) * 2] = col(outer);
2904
+ uvs[(ringOuter + j) * 2 + 1] = v;
2905
+ });
2853
2906
  const fan = [];
2854
- for (let i = 0; i <= CAP_SEGMENTS; i++) {
2855
- const a = Math.PI * i / CAP_SEGMENTS;
2856
- cap.push(
2857
- lx(g.elbow.x + g.capRadius * Math.cos(a)),
2858
- ly(g.elbow.y + g.capRadius * Math.sin(a))
2859
- );
2860
- if (i > 0) fan.push(4, 4 + i, 5 + i);
2861
- }
2862
- const part = (id, deformer, at, mesh) => ({
2907
+ for (let i = 0; i < rays - 1; i++) fan.push(0, 1 + i, 2 + i);
2908
+ const ring = [];
2909
+ for (let j = 0; j < ringRays.length - 1; j++) {
2910
+ if (j === top) continue;
2911
+ const [p, q] = [ringInner + j, ringOuter + j];
2912
+ ring.push(p, q, q + 1, p, q + 1, p + 1);
2913
+ }
2914
+ const part = (id, deformer, z, mesh) => ({
2863
2915
  id,
2864
2916
  color: [1, 1, 1, 1],
2865
2917
  width: bw(b),
2866
2918
  height: bh(b),
2867
2919
  transform: { x: cx(b), y: cy(b) },
2868
- order: at,
2920
+ order: z,
2869
2921
  deformer,
2870
2922
  mesh
2871
2923
  });
2872
2924
  const ids = ARM_IDS[role];
2873
2925
  return [
2874
- part(role, ids.armDeformer, order, meshOf(band(0.5, seam), quad)),
2926
+ part(ids.cap, ids.forearmDeformer, order, {
2927
+ vertices,
2928
+ uvs,
2929
+ indices: [...fan, ...ring]
2930
+ }),
2931
+ part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
2875
2932
  part(
2876
2933
  ids.forearm,
2877
2934
  ids.forearmDeformer,
2878
- order + 1,
2879
- meshOf([...band(seam, -0.5), ...cap], [...quad, ...fan])
2935
+ order + 2,
2936
+ meshOf(band(seam, -0.5), quad)
2880
2937
  )
2881
2938
  ];
2882
2939
  }
2883
2940
  function armDeformers(role, g) {
2884
2941
  const ids = ARM_IDS[role];
2885
2942
  const params = ARM_PARAMS[role];
2886
- const rotate = (parameter) => ({
2943
+ const rotate = (parameter, range, sign) => ({
2887
2944
  parameter,
2888
2945
  channel: "rotate",
2889
- from: g.side * ARM_RANGE[0],
2890
- to: g.side * ARM_RANGE[1]
2946
+ from: sign * range[0],
2947
+ to: sign * range[1]
2891
2948
  });
2892
2949
  return [
2893
2950
  {
2894
2951
  id: ids.armDeformer,
2895
2952
  parent: BODY_WARP_ID,
2896
2953
  pivot: { ...g.shoulder },
2897
- bindings: [rotate(params.arm)]
2954
+ bindings: [rotate(params.arm, ARM_RANGE, g.side)]
2898
2955
  },
2899
2956
  {
2900
2957
  id: ids.forearmDeformer,
2901
2958
  parent: ids.armDeformer,
2902
2959
  pivot: { ...g.elbow },
2903
- bindings: [rotate(params.elbow)]
2960
+ bindings: [rotate(params.elbow, ELBOW_RANGE, -g.side)]
2904
2961
  }
2905
2962
  ];
2906
2963
  }
@@ -2909,8 +2966,8 @@ function armDeformers(role, g) {
2909
2966
  var ROLE_TABLE = [
2910
2967
  { role: "hair_back", family: "hair_back" },
2911
2968
  { role: "body", family: "body" },
2912
- { role: "arm_L", family: "arm", parts: ["arm_L", "forearm_L"] },
2913
- { role: "arm_R", family: "arm", parts: ["arm_R", "forearm_R"] },
2969
+ { role: "arm_L", family: "arm", parts: ["elbow_L", "arm_L", "forearm_L"] },
2970
+ { role: "arm_R", family: "arm", parts: ["elbow_R", "arm_R", "forearm_R"] },
2914
2971
  { role: "face", family: "face", required: true },
2915
2972
  { role: "blush_L", family: "face" },
2916
2973
  { role: "blush_R", family: "face" },
@@ -5723,13 +5780,14 @@ function declareParameters(roles) {
5723
5780
  add(import_format8.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
5724
5781
  }
5725
5782
  const [armMin, armMax] = ARM_RANGE;
5783
+ const [elbowMin, elbowMax] = ELBOW_RANGE;
5726
5784
  if (roles.has("arm_L")) {
5727
5785
  add(import_format8.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
5728
- add(import_format8.StandardParameter.ElbowLeft, "Elbow L", armMin, armMax, 0);
5786
+ add(import_format8.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
5729
5787
  }
5730
5788
  if (roles.has("arm_R")) {
5731
5789
  add(import_format8.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
5732
- add(import_format8.StandardParameter.ElbowRight, "Elbow R", armMin, armMax, 0);
5790
+ add(import_format8.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
5733
5791
  }
5734
5792
  add(import_format8.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
5735
5793
  add(import_format8.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);