miijs 2.2.0 → 2.2.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.
@@ -0,0 +1,123 @@
1
+ // ------------------------------
2
+ // Helpers
3
+ // ------------------------------
4
+ const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
5
+
6
+ // ------------------------------
7
+ // Height (0..127) <-> feet/inches
8
+ // Calibrated so height=64 -> 67 inches exactly.
9
+ // Range: 36" (3'0") .. 84" (7'0").
10
+ // ------------------------------
11
+ const HEIGHT_MIN_IN = 36;
12
+ const HEIGHT_MAX_IN = 84;
13
+ const HEIGHT_RANGE_IN = HEIGHT_MAX_IN - HEIGHT_MIN_IN;
14
+
15
+ // Calibration target: slider mid (64) should be 67"
16
+ const HEIGHT_MID_SLIDER = 64;
17
+ const HEIGHT_MID_IN = 67;
18
+
19
+ // Compute exponent k so that x0^k = y0 (bias curve, invertible)
20
+ const x0 = HEIGHT_MID_SLIDER / 127; // ~0.50394
21
+ const y0 = (HEIGHT_MID_IN - HEIGHT_MIN_IN) / HEIGHT_RANGE_IN; // 31/48 ≈ 0.64583
22
+ const HEIGHT_EXP = Math.log(y0) / Math.log(x0); // ≈ 0.638 (computed once)
23
+
24
+ /** 0..127 -> { feet, inches, totalInches } */
25
+ function miiHeightToFeetInches(heightValue) {
26
+ const hv = clamp(heightValue | 0, 0, 127);
27
+ const x = hv / 127;
28
+ const s = Math.pow(x, HEIGHT_EXP); // calibrated bias
29
+ const inches = HEIGHT_MIN_IN + s * HEIGHT_RANGE_IN;
30
+
31
+ const totalInches = Math.round(inches);
32
+ const feet = Math.floor(totalInches / 12);
33
+ const inchesR = totalInches % 12;
34
+
35
+ return { feet, inches: inchesR, totalInches };
36
+ }
37
+
38
+ /** inches -> 0..127 (inverse of the calibrated mapping) */
39
+ function inchesToMiiHeight(targetInches) {
40
+ const y = clamp((targetInches - HEIGHT_MIN_IN) / HEIGHT_RANGE_IN, 0, 1);
41
+ const x = Math.pow(y, 1 / HEIGHT_EXP);
42
+ return Math.round(x * 127);
43
+ }
44
+
45
+ // ------------------------------
46
+ // Weight (0..127) + height(0..127) <-> pounds
47
+ // Baseline by BMI; composition slider is nonlinear & invertible.
48
+ // Center (64) -> multiplier 1.0.
49
+ // ------------------------------
50
+ const BMI_AVG = 23.5; // gives ~150 lb at 67"
51
+ const WEIGHT_SPREAD = 0.35; // ±35% around baseline at extremes
52
+ const WEIGHT_GAMMA = 1.6; // steeper near ends, softer center
53
+
54
+ /** weight slider + height slider -> pounds */
55
+ function miiWeightToPounds(weightValue, heightValue) {
56
+ const wv = clamp(weightValue | 0, 0, 127);
57
+ const hv = clamp(heightValue | 0, 0, 127);
58
+
59
+ // Height in inches using the calibrated mapping
60
+ const { totalInches: H } = miiHeightToFeetInches(hv);
61
+
62
+ // BMI baseline
63
+ const base = (BMI_AVG * H * H) / 703;
64
+
65
+ // Nonlinear symmetric multiplier around 1.0 (64 -> 0 offset)
66
+ const d = clamp((wv - 64) / 63, -1, 1);
67
+ const multiplier = 1 + Math.sign(d) * WEIGHT_SPREAD * Math.pow(Math.abs(d), WEIGHT_GAMMA);
68
+
69
+ return Math.round(base * multiplier);
70
+ }
71
+
72
+ /** pounds + height slider -> weight slider (inverse of above) */
73
+ function poundsToMiiWeight(pounds, heightValue) {
74
+ const hv = clamp(heightValue | 0, 0, 127);
75
+ const { totalInches: H } = miiHeightToFeetInches(hv);
76
+ const base = (BMI_AVG * H * H) / 703;
77
+
78
+ // Guard against tiny numeric drift outside the spread
79
+ const mult = clamp(pounds / base, 1 - WEIGHT_SPREAD * 1.0001, 1 + WEIGHT_SPREAD * 1.0001);
80
+
81
+ let d;
82
+ if (Math.abs(mult - 1) < 1e-9) {
83
+ d = 0;
84
+ } else if (mult > 1) {
85
+ d = Math.pow((mult - 1) / WEIGHT_SPREAD, 1 / WEIGHT_GAMMA);
86
+ } else {
87
+ d = -Math.pow((1 - mult) / WEIGHT_SPREAD, 1 / WEIGHT_GAMMA);
88
+ }
89
+
90
+ const wv = Math.round(64 + 63 * clamp(d, -1, 1));
91
+ return clamp(wv, 0, 127);
92
+ }
93
+
94
+ // ------------------------------
95
+ // Quick checks (should match your expectations)
96
+ // ------------------------------
97
+ // Average points
98
+ // console.log(miiHeightToFeetInches(64), miiWeightToPounds(64, 64)); // ~5'7", ~150 lb
99
+ // Extremes
100
+ // console.log(miiHeightToFeetInches(0), miiWeightToPounds(0, 0)); // 3'0", ~28 lb
101
+ // console.log(miiHeightToFeetInches(127), miiWeightToPounds(127, 127)); // 7'0", ~318 lb
102
+ // Inverses
103
+ // const h = miiHeightToFeetInches(64);
104
+ // console.log(inchesToMiiHeight(h.totalInches)); // ~64
105
+ // const lbs = miiWeightToPounds(96, 64);
106
+ // console.log(lbs); // ~135
107
+ // console.log(poundsToMiiWeight(lbs, 64)); // ~96
108
+
109
+
110
+ // ------------------------------
111
+ // Quick sanity checks
112
+ // ------------------------------
113
+ // Average points
114
+ console.log(miiHeightToFeetInches(64), miiWeightToPounds(64, 64)); // ~5'0", ~150 lb
115
+ // // Extremes
116
+ console.log(miiHeightToFeetInches(0), miiWeightToPounds(0, 0));//3'0, 28 lb
117
+ console.log(miiHeightToFeetInches(127), miiWeightToPounds(127, 127));//7'0 318 lb
118
+ // // Inverses
119
+ const h = miiHeightToFeetInches(64);
120
+ console.log(inchesToMiiHeight(h.totalInches)); // ~64
121
+ const lbs = miiWeightToPounds(96, 64);
122
+ console.log(lbs);//168
123
+ console.log(poundsToMiiWeight(lbs, 64)); // ~96
package/index.js CHANGED
@@ -2179,11 +2179,11 @@ function convertMii(jsonIn,typeTo){
2179
2179
  }
2180
2180
  miiTo.nose.type=convTables.nose3DSToWii[mii.nose.page][mii.nose.type];
2181
2181
  miiTo.mouth.type=convTables.mouth3DSToWii[mii.mouth.page][mii.mouth.type];
2182
- miiTo.mouth.color=mii.mouth.col>2?0:mii.mouth.col;
2182
+ miiTo.mouth.color=mii.mouth.color>2?0:mii.mouth.color;
2183
2183
  miiTo.hair.type=convTables.hair3DSToWii[mii.hair.page][mii.hair.type];
2184
2184
  miiTo.eyebrows.type=convTables.eyebrows3DSToWii[mii.eyebrows.page][mii.eyebrows.type];
2185
2185
  miiTo.eyes.type=convTables.eyes3DSToWii[mii.eyes.page][mii.eyes.type];
2186
- miiTo.glasses.col=mii.glasses.col;
2186
+ miiTo.glasses.color=mii.glasses.color;
2187
2187
  if(miiTo.beard.mustache.type===4){
2188
2188
  miiTo.beard.mustache.type=2;
2189
2189
  }
@@ -2211,7 +2211,7 @@ function convertMii(jsonIn,typeTo){
2211
2211
  }
2212
2212
  miiTo.eyes.squash=3;
2213
2213
  miiTo.eyebrows.squash=3;
2214
- miiTo.mouth.col=mouthCols3DS[mii.mouth.col];//qk
2214
+ miiTo.mouth.color=mii.mouth.color;
2215
2215
  miiTo.mouth.squash=3;
2216
2216
  miiTo.console="3ds";
2217
2217
  }
@@ -3054,7 +3054,7 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3054
3054
  main_img.blit(canvas, 212 - 100 / 2, 212 - 100 / 2);
3055
3055
  const font = await Jimp.loadFont(Jimp.FONT_SANS_16_BLACK)
3056
3056
 
3057
- main_img.print(font, 0, 55, {
3057
+ main_img.print(font, 0, 70, {
3058
3058
  text: miiJson.meta.name,
3059
3059
  alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
3060
3060
  alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
@@ -3077,61 +3077,64 @@ async function write3DSQR(miiJson, outPath, fflRes = getFFLRes()) {
3077
3077
  return imageBuffer;
3078
3078
  }
3079
3079
  function make3DSChild(dad,mom,options={}){
3080
- if(!["3ds","wii u"].includes(dad.console?.toLowerCase())){
3080
+ if(!["3ds","wii u"].includes(dad.meta.console?.toLowerCase())){
3081
3081
  dad=convertMii(dad,"wii");
3082
3082
  }
3083
- if(!["3ds","wii u"].includes(mom.console?.toLowerCase())){
3083
+ if(!["3ds","wii u"].includes(mom.meta.console?.toLowerCase())){
3084
3084
  mom=convertMii(dad,"wii");
3085
3085
  }
3086
- var g=options.gender||Math.floor(Math.random()*2)===1?"Male":"Female";
3086
+ var g=options.gender||Math.floor(Math.random()*2);
3087
3087
  var child={
3088
- "info":{
3088
+ "general":{
3089
3089
  "birthMonth":new Date().getMonth()+1,
3090
3090
  "birthday":new Date().getDay(),
3091
3091
  "height":64,
3092
3092
  "weight":64,
3093
- "creatorName":"",
3094
3093
  "gender":g,
3095
- "name":options.name||kidNames[g][Math.floor(Math.random()*kidNames[g].length)],
3096
3094
  "favColor":options.favColor||favCols[Math.floor(Math.random()*favCols.length)]
3097
3095
  },
3096
+ "meta":{
3097
+ "name":options.name||kidNames[g][Math.floor(Math.random()*kidNames[g].length)],
3098
+ "creatorName":"",
3099
+ },
3098
3100
  "perms":{
3099
3101
  "sharing":true,
3100
3102
  "copying":true
3101
3103
  },
3102
3104
  "hair":{
3103
- "style":[8,3],//Hardcoded
3104
- "col":Math.floor(Math.random()*2)===1?dad.hair.col:mom.hair.col,
3105
+ "page":8,//Hardcoded, needs to be generated
3106
+ "type":3,// ^
3107
+ "color":Math.floor(Math.random()*2)===1?dad.hair.color:mom.hair.color,
3105
3108
  "flipped":Math.floor(Math.random()*2)===0?true:false
3106
3109
  },
3107
3110
  "face":{
3108
3111
  "shape":Math.floor(Math.random()*2)===1?dad.face.shape:mom.face.shape,
3109
3112
  "feature":Math.floor(Math.random()*2)===1?dad.face.feature:mom.face.feature,
3110
- "makeup":g==="Male"?"None":Math.floor(Math.random()*2)===1?dad.face.makeup:mom.face.makeup
3113
+ "makeup":g===0?0:Math.floor(Math.random()*2)===1?dad.face.makeup:mom.face.makeup
3111
3114
  },
3112
3115
  "eyes":Math.floor(Math.random()*2)===1?dad.eyes:mom.eyes,
3113
3116
  "eyebrows":Math.floor(Math.random()*2)===1?dad.eyebrows:mom.eyebrows,
3114
3117
  "nose":Math.floor(Math.random()*2)===1?dad.nose:mom.nose,
3115
3118
  "mouth":Math.floor(Math.random()*2)===1?dad.mouth:mom.mouth,
3116
- "facialHair":g==="Female"?{
3117
- "mustacheType": 0,
3118
- "beardType": 0,
3119
- "col": "Black",
3120
- "mustacheSize": 4,
3121
- "mustacheYPos": 10
3122
- }:Math.floor(Math.random()*2)===0?dad.facialHair:mom.facialHair,
3119
+ "beard":{//Beards can never be generated for children allegedly, confirm before finishing
3120
+ "mustache":{
3121
+ "type":0,
3122
+ "mustacheSize": 4,
3123
+ "mustacheYPos": 10
3124
+ },
3125
+ "type": 0,
3126
+ "color": 0
3127
+ },
3123
3128
  "glasses":Math.floor(Math.random()*2)===1?dad.glasses:mom.glasses,
3124
- "mole":Math.floor(Math.random()*2)===1?dad.mole:mom.mole,
3125
- "creatorName":""
3129
+ "mole":Math.floor(Math.random()*2)===1?dad.mole:mom.mole
3126
3130
  };
3127
- child.eyebrows.col=child.hair.col;
3128
- var c=[skinCols.indexOf(mom.face.col),skinCols.indexOf(dad.face.col)];
3131
+ child.eyebrows.color=child.hair.color;
3132
+ var c=[mom.face.color,dad.face.color];
3129
3133
  if(c[0]>c[1]){
3130
3134
  c[1]=c[0];
3131
- c[0]=skinCols.indexOf(dad.face.col);
3135
+ c[0]=dad.face.color;
3132
3136
  }
3133
- child.face.col=skinCols[c[0]+Math.round((c[1]-c[0])/2)];
3134
- child.name=child.info.name;
3137
+ child.face.color=c[0]+Math.round((c[1]-c[0])/2);
3135
3138
  child.type="3DS";
3136
3139
  return child;
3137
3140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "miijs",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Work with Mii characters in every possible way needed for your project.",
5
5
  "main": "index.js",
6
6
  "scripts": {