gputex 0.6.0 → 0.8.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 +86 -55
- package/dist/index.d.ts +15 -7
- package/dist/index.js +533 -587
- package/dist/testing.d.ts +8 -5
- package/dist/testing.js +194 -36
- package/dist/three.js +533 -587
- package/package.json +1 -1
package/dist/testing.d.ts
CHANGED
|
@@ -110,8 +110,10 @@ type ASTC4x4Block = Uint8Array;
|
|
|
110
110
|
* grayscale + opaque → luminance, opaque → RGB, otherwise RGBA.
|
|
111
111
|
*
|
|
112
112
|
* Algorithm per class:
|
|
113
|
-
* 1. Quantise input to 8-bit; classify
|
|
114
|
-
*
|
|
113
|
+
* 1. Quantise input to 8-bit; classify (opaque colour tries both CEM 8
|
|
114
|
+
* bit budgets and keeps the lower-error one).
|
|
115
|
+
* 2. Farthest-pair over the class's channels → initial (e0, e1),
|
|
116
|
+
* quantised to the class's endpoint range.
|
|
115
117
|
* 3. Assign per-texel indices by nearest palette entry.
|
|
116
118
|
* 4. One LSQ refit pass; accept only if total error strictly decreases.
|
|
117
119
|
* 5. CEM 8/12: flip endpoints (and reflect weights) if
|
|
@@ -121,13 +123,14 @@ type ASTC4x4Block = Uint8Array;
|
|
|
121
123
|
declare function encodeASTC4x4Block(pixels: ASTC4x4Pixels): ASTC4x4Block;
|
|
122
124
|
/**
|
|
123
125
|
* Decode an ASTC 4×4 block produced by this encoder (or by any other
|
|
124
|
-
* encoder that respects our narrow subset: block modes 0x042/0x053/
|
|
125
|
-
* single partition, CEM 0/8/12
|
|
126
|
+
* encoder that respects our narrow subset: block modes 0x042/0x053/0x242/
|
|
127
|
+
* 0x253, single partition, CEM 0/8/12 — QUANT_192 endpoints with 0x242,
|
|
128
|
+
* 8-bit endpoints otherwise). Handles the
|
|
126
129
|
* blue-contraction branch even though our encoder doesn't produce it, so
|
|
127
130
|
* externally-supplied blocks round-trip predictably.
|
|
128
131
|
*
|
|
129
132
|
* The CEM is validated against the block mode's expected pairing (we only
|
|
130
|
-
* ever emit the
|
|
133
|
+
* ever emit the four fixed combinations above).
|
|
131
134
|
*
|
|
132
135
|
* Output: 16 RGBA pixels as 64 floats in [0, 1].
|
|
133
136
|
*/
|
package/dist/testing.js
CHANGED
|
@@ -922,33 +922,49 @@ function decodeBC7Block(block) {
|
|
|
922
922
|
// src/astc4x4_ref.ts
|
|
923
923
|
var BLOCK_MODE_4x4_2BIT = 66;
|
|
924
924
|
var BLOCK_MODE_4x4_3BIT = 83;
|
|
925
|
+
var BLOCK_MODE_4x4_4BIT = 578;
|
|
925
926
|
var BLOCK_MODE_4x4_5BIT = 595;
|
|
926
927
|
var CEM_LUM_DIRECT = 0;
|
|
927
928
|
var CEM_RGB_DIRECT = 8;
|
|
928
929
|
var CEM_RGBA_DIRECT = 12;
|
|
929
930
|
var WEIGHT_UNQ_4 = [0, 21, 43, 64];
|
|
930
931
|
var WEIGHT_UNQ_8 = [0, 9, 18, 27, 37, 46, 55, 64];
|
|
932
|
+
var WEIGHT_UNQ_16 = Array.from({ length: 16 }, (_, w) => {
|
|
933
|
+
const u = w << 2 | w >> 2;
|
|
934
|
+
return u > 32 ? u + 1 : u;
|
|
935
|
+
});
|
|
931
936
|
var WEIGHT_UNQ_32 = Array.from({ length: 32 }, (_, w) => w <= 15 ? 2 * w : 2 * w + 2);
|
|
932
937
|
var CLASS_LUM = {
|
|
933
938
|
cem: CEM_LUM_DIRECT,
|
|
934
939
|
blockMode: BLOCK_MODE_4x4_5BIT,
|
|
935
940
|
channels: [0],
|
|
936
941
|
weightBits: 5,
|
|
937
|
-
unq: WEIGHT_UNQ_32
|
|
942
|
+
unq: WEIGHT_UNQ_32,
|
|
943
|
+
endpointRange: 256
|
|
938
944
|
};
|
|
939
945
|
var CLASS_RGB = {
|
|
940
946
|
cem: CEM_RGB_DIRECT,
|
|
941
947
|
blockMode: BLOCK_MODE_4x4_3BIT,
|
|
942
948
|
channels: [0, 1, 2],
|
|
943
949
|
weightBits: 3,
|
|
944
|
-
unq: WEIGHT_UNQ_8
|
|
950
|
+
unq: WEIGHT_UNQ_8,
|
|
951
|
+
endpointRange: 256
|
|
952
|
+
};
|
|
953
|
+
var CLASS_RGB_Q192 = {
|
|
954
|
+
cem: CEM_RGB_DIRECT,
|
|
955
|
+
blockMode: BLOCK_MODE_4x4_4BIT,
|
|
956
|
+
channels: [0, 1, 2],
|
|
957
|
+
weightBits: 4,
|
|
958
|
+
unq: WEIGHT_UNQ_16,
|
|
959
|
+
endpointRange: 192
|
|
945
960
|
};
|
|
946
961
|
var CLASS_RGBA = {
|
|
947
962
|
cem: CEM_RGBA_DIRECT,
|
|
948
963
|
blockMode: BLOCK_MODE_4x4_2BIT,
|
|
949
964
|
channels: [0, 1, 2, 3],
|
|
950
965
|
weightBits: 2,
|
|
951
|
-
unq: WEIGHT_UNQ_4
|
|
966
|
+
unq: WEIGHT_UNQ_4,
|
|
967
|
+
endpointRange: 256
|
|
952
968
|
};
|
|
953
969
|
function clamp2(v, lo, hi) {
|
|
954
970
|
return v < lo ? lo : v > hi ? hi : v;
|
|
@@ -959,6 +975,84 @@ function to82(v) {
|
|
|
959
975
|
function interp16(e0, e1, w) {
|
|
960
976
|
return (64 - w) * e0 * 257 + w * e1 * 257 + 32 >> 6;
|
|
961
977
|
}
|
|
978
|
+
function unq192(value) {
|
|
979
|
+
const trit = value >> 6;
|
|
980
|
+
const bits = value & 63;
|
|
981
|
+
const A = bits & 1 ? 511 : 0;
|
|
982
|
+
const f = bits >> 5 & 1;
|
|
983
|
+
const B = (bits >> 1 & 31) << 4 | f;
|
|
984
|
+
const T = (trit * 5 + B ^ A) & 511;
|
|
985
|
+
return (A & 128 | T >> 2) & 255;
|
|
986
|
+
}
|
|
987
|
+
var Q192_VALUE_OF = (() => {
|
|
988
|
+
const t = new Int16Array(256).fill(-1);
|
|
989
|
+
for (let v = 0; v < 192; v++) t[unq192(v)] = v;
|
|
990
|
+
return t;
|
|
991
|
+
})();
|
|
992
|
+
function nearest192(x) {
|
|
993
|
+
const v = clamp2(Math.round(x), 0, 255);
|
|
994
|
+
if (Q192_VALUE_OF[v] >= 0) return v;
|
|
995
|
+
const lo = v - 1;
|
|
996
|
+
const hi = v + 1;
|
|
997
|
+
const loOk = lo >= 0 && Q192_VALUE_OF[lo] >= 0;
|
|
998
|
+
const hiOk = hi <= 255 && Q192_VALUE_OF[hi] >= 0;
|
|
999
|
+
if (loOk && hiOk) return x - lo <= hi - x ? lo : hi;
|
|
1000
|
+
if (loOk) return x - lo <= 2 ? lo : v + 2;
|
|
1001
|
+
return hiOk ? hi : v - 2;
|
|
1002
|
+
}
|
|
1003
|
+
function decodeTrits(T) {
|
|
1004
|
+
const bit = (x, i) => x >> i & 1;
|
|
1005
|
+
let C;
|
|
1006
|
+
let t3;
|
|
1007
|
+
let t4;
|
|
1008
|
+
if ((T >> 2 & 7) === 7) {
|
|
1009
|
+
C = (T >> 5 & 7) << 2 | T & 3;
|
|
1010
|
+
t4 = 2;
|
|
1011
|
+
t3 = 2;
|
|
1012
|
+
} else {
|
|
1013
|
+
C = T & 31;
|
|
1014
|
+
if ((T >> 5 & 3) === 3) {
|
|
1015
|
+
t4 = 2;
|
|
1016
|
+
t3 = bit(T, 7);
|
|
1017
|
+
} else {
|
|
1018
|
+
t4 = bit(T, 7);
|
|
1019
|
+
t3 = T >> 5 & 3;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
let t0;
|
|
1023
|
+
let t1;
|
|
1024
|
+
let t2;
|
|
1025
|
+
if ((C & 3) === 3) {
|
|
1026
|
+
t2 = 2;
|
|
1027
|
+
t1 = bit(C, 4);
|
|
1028
|
+
t0 = bit(C, 3) << 1 | bit(C, 2) & ~bit(C, 3) & 1;
|
|
1029
|
+
} else if ((C >> 2 & 3) === 3) {
|
|
1030
|
+
t2 = 2;
|
|
1031
|
+
t1 = 2;
|
|
1032
|
+
t0 = C & 3;
|
|
1033
|
+
} else {
|
|
1034
|
+
t2 = bit(C, 4);
|
|
1035
|
+
t1 = C >> 2 & 3;
|
|
1036
|
+
t0 = bit(C, 1) << 1 | bit(C, 0) & ~bit(C, 1) & 1;
|
|
1037
|
+
}
|
|
1038
|
+
return [t0, t1, t2, t3, t4];
|
|
1039
|
+
}
|
|
1040
|
+
function encodeTrits(t0, t1, t2, t3, t4) {
|
|
1041
|
+
let C;
|
|
1042
|
+
if (t2 === 2 && t1 === 2) C = 12 | t0;
|
|
1043
|
+
else if (t2 === 2) C = t1 << 4 | t0 << 2 | 3;
|
|
1044
|
+
else C = t2 << 4 | t1 << 2 | t0;
|
|
1045
|
+
if (t3 === 2 && t4 === 2) return C >> 2 << 5 | 7 << 2 | C & 3;
|
|
1046
|
+
if (t4 === 2) return t3 << 7 | 3 << 5 | C;
|
|
1047
|
+
return t4 << 7 | t3 << 5 | C;
|
|
1048
|
+
}
|
|
1049
|
+
var TRIT_SLICES = [
|
|
1050
|
+
[0, 2],
|
|
1051
|
+
[2, 2],
|
|
1052
|
+
[4, 1],
|
|
1053
|
+
[5, 2],
|
|
1054
|
+
[7, 1]
|
|
1055
|
+
];
|
|
962
1056
|
var BitWriter1282 = class {
|
|
963
1057
|
bits = 0n;
|
|
964
1058
|
write(pos, nBits, value) {
|
|
@@ -1070,39 +1164,61 @@ function refitEndpoints2(vals, C, indices, unq) {
|
|
|
1070
1164
|
}
|
|
1071
1165
|
return { e0, e1 };
|
|
1072
1166
|
}
|
|
1073
|
-
function
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const pixels8 = new Uint8Array(64);
|
|
1078
|
-
for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
|
|
1079
|
-
let gray = true;
|
|
1080
|
-
let opaque = true;
|
|
1081
|
-
for (let k = 0; k < 16; k++) {
|
|
1082
|
-
const r = pixels8[k * 4];
|
|
1083
|
-
if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
|
|
1084
|
-
if (pixels8[k * 4 + 3] !== 255) opaque = false;
|
|
1085
|
-
}
|
|
1086
|
-
const cls = opaque ? gray ? CLASS_LUM : CLASS_RGB : CLASS_RGBA;
|
|
1167
|
+
function quantEndpoint(cls, e) {
|
|
1168
|
+
return cls.endpointRange === 192 ? e.map(nearest192) : e.map((v) => clamp2(Math.round(v), 0, 255));
|
|
1169
|
+
}
|
|
1170
|
+
function fitClass(cls, pixels8) {
|
|
1087
1171
|
const C = cls.channels.length;
|
|
1088
1172
|
const vals = new Float64Array(16 * C);
|
|
1089
1173
|
for (let k = 0; k < 16; k++) {
|
|
1090
1174
|
for (let c = 0; c < C; c++) vals[k * C + c] = pixels8[k * 4 + cls.channels[c]];
|
|
1091
1175
|
}
|
|
1092
1176
|
const fp = farthestPair2(vals, C);
|
|
1093
|
-
let e0 =
|
|
1094
|
-
|
|
1177
|
+
let e0 = quantEndpoint(
|
|
1178
|
+
cls,
|
|
1179
|
+
Array.from({ length: C }, (_, c) => vals[fp.i0 * C + c])
|
|
1180
|
+
);
|
|
1181
|
+
let e1 = quantEndpoint(
|
|
1182
|
+
cls,
|
|
1183
|
+
Array.from({ length: C }, (_, c) => vals[fp.i1 * C + c])
|
|
1184
|
+
);
|
|
1095
1185
|
let { indices, err } = totalSqError(vals, C, e0, e1, cls.unq);
|
|
1096
1186
|
const refit = refitEndpoints2(vals, C, indices, cls.unq);
|
|
1097
1187
|
if (refit) {
|
|
1098
|
-
const
|
|
1188
|
+
const r0 = quantEndpoint(cls, refit.e0);
|
|
1189
|
+
const r1 = quantEndpoint(cls, refit.e1);
|
|
1190
|
+
const second = totalSqError(vals, C, r0, r1, cls.unq);
|
|
1099
1191
|
if (second.err < err) {
|
|
1100
|
-
e0 =
|
|
1101
|
-
e1 =
|
|
1192
|
+
e0 = r0;
|
|
1193
|
+
e1 = r1;
|
|
1102
1194
|
indices = second.indices;
|
|
1103
1195
|
err = second.err;
|
|
1104
1196
|
}
|
|
1105
1197
|
}
|
|
1198
|
+
return { e0, e1, indices, err };
|
|
1199
|
+
}
|
|
1200
|
+
function encodeASTC4x4Block(pixels) {
|
|
1201
|
+
if (pixels.length !== 64) {
|
|
1202
|
+
throw new Error(`encodeASTC4x4Block: expected 64 values (16 RGBA), got ${pixels.length}`);
|
|
1203
|
+
}
|
|
1204
|
+
const pixels8 = new Uint8Array(64);
|
|
1205
|
+
for (let k = 0; k < 64; k++) pixels8[k] = to82(pixels[k]);
|
|
1206
|
+
let gray = true;
|
|
1207
|
+
let opaque = true;
|
|
1208
|
+
for (let k = 0; k < 16; k++) {
|
|
1209
|
+
const r = pixels8[k * 4];
|
|
1210
|
+
if (pixels8[k * 4 + 1] !== r || pixels8[k * 4 + 2] !== r) gray = false;
|
|
1211
|
+
if (pixels8[k * 4 + 3] !== 255) opaque = false;
|
|
1212
|
+
}
|
|
1213
|
+
const classes = opaque ? gray ? [CLASS_LUM] : [CLASS_RGB, CLASS_RGB_Q192] : [CLASS_RGBA];
|
|
1214
|
+
let best = null;
|
|
1215
|
+
for (const cls2 of classes) {
|
|
1216
|
+
const fit = fitClass(cls2, pixels8);
|
|
1217
|
+
if (!best || fit.err < best.err) best = { cls: cls2, ...fit };
|
|
1218
|
+
}
|
|
1219
|
+
const { cls } = best;
|
|
1220
|
+
let { e0, e1, indices } = best;
|
|
1221
|
+
const C = cls.channels.length;
|
|
1106
1222
|
{
|
|
1107
1223
|
const nSum = Math.min(C, 3);
|
|
1108
1224
|
let s0 = 0;
|
|
@@ -1128,9 +1244,29 @@ function packBlock3(cls, e0, e1, indices) {
|
|
|
1128
1244
|
bw.write(0, 11, cls.blockMode);
|
|
1129
1245
|
bw.write(11, 2, 0);
|
|
1130
1246
|
bw.write(13, 4, cls.cem);
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1247
|
+
const values = [];
|
|
1248
|
+
for (let c = 0; c < e0.length; c++) values.push(e0[c], e1[c]);
|
|
1249
|
+
if (cls.endpointRange === 192) {
|
|
1250
|
+
let pos = 17;
|
|
1251
|
+
for (let g = 0; g < values.length; g += 5) {
|
|
1252
|
+
const grp = values.slice(g, g + 5).map((v) => {
|
|
1253
|
+
const q = Q192_VALUE_OF[v];
|
|
1254
|
+
if (q < 0) throw new Error(`packBlock: ${v} is not a QUANT_192 level`);
|
|
1255
|
+
return q;
|
|
1256
|
+
});
|
|
1257
|
+
const t = [0, 0, 0, 0, 0];
|
|
1258
|
+
grp.forEach((q, i) => t[i] = q >> 6);
|
|
1259
|
+
const T = encodeTrits(t[0], t[1], t[2], t[3], t[4]);
|
|
1260
|
+
grp.forEach((q, i) => {
|
|
1261
|
+
bw.write(pos, 6, q & 63);
|
|
1262
|
+
pos += 6;
|
|
1263
|
+
const [start, count] = TRIT_SLICES[i];
|
|
1264
|
+
bw.write(pos, count, T >> start & (1 << count) - 1);
|
|
1265
|
+
pos += count;
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
} else {
|
|
1269
|
+
values.forEach((v, i) => bw.write(17 + i * 8, 8, v));
|
|
1134
1270
|
}
|
|
1135
1271
|
for (let k = 0; k < 16; k++) {
|
|
1136
1272
|
const w = indices[k];
|
|
@@ -1140,19 +1276,20 @@ function packBlock3(cls, e0, e1, indices) {
|
|
|
1140
1276
|
}
|
|
1141
1277
|
return bw.toBytes();
|
|
1142
1278
|
}
|
|
1143
|
-
var
|
|
1144
|
-
[BLOCK_MODE_4x4_2BIT]: CLASS_RGBA,
|
|
1145
|
-
[BLOCK_MODE_4x4_3BIT]: CLASS_RGB,
|
|
1146
|
-
[
|
|
1279
|
+
var CLASS_BY_MODE_CEM = {
|
|
1280
|
+
[`${BLOCK_MODE_4x4_2BIT}:${CEM_RGBA_DIRECT}`]: CLASS_RGBA,
|
|
1281
|
+
[`${BLOCK_MODE_4x4_3BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB,
|
|
1282
|
+
[`${BLOCK_MODE_4x4_4BIT}:${CEM_RGB_DIRECT}`]: CLASS_RGB_Q192,
|
|
1283
|
+
[`${BLOCK_MODE_4x4_5BIT}:${CEM_LUM_DIRECT}`]: CLASS_LUM
|
|
1147
1284
|
};
|
|
1285
|
+
var SUPPORTED_MODES = /* @__PURE__ */ new Set([BLOCK_MODE_4x4_2BIT, BLOCK_MODE_4x4_3BIT, BLOCK_MODE_4x4_4BIT, BLOCK_MODE_4x4_5BIT]);
|
|
1148
1286
|
function decodeASTC4x4Block(block) {
|
|
1149
1287
|
if (block.length !== 16) {
|
|
1150
1288
|
throw new Error(`decodeASTC4x4Block: expected 16 bytes, got ${block.length}`);
|
|
1151
1289
|
}
|
|
1152
1290
|
const br = new BitReader1282(block);
|
|
1153
1291
|
const mode = br.read(0, 11);
|
|
1154
|
-
|
|
1155
|
-
if (!cls) {
|
|
1292
|
+
if (!SUPPORTED_MODES.has(mode)) {
|
|
1156
1293
|
throw new Error(`decodeASTC4x4Block: unsupported block mode 0x${mode.toString(16)}`);
|
|
1157
1294
|
}
|
|
1158
1295
|
const partCount = br.read(11, 2);
|
|
@@ -1160,12 +1297,31 @@ function decodeASTC4x4Block(block) {
|
|
|
1160
1297
|
throw new Error(`decodeASTC4x4Block: multi-partition blocks not supported (count=${partCount + 1})`);
|
|
1161
1298
|
}
|
|
1162
1299
|
const cem = br.read(13, 4);
|
|
1163
|
-
|
|
1164
|
-
|
|
1300
|
+
const cls = CLASS_BY_MODE_CEM[`${mode}:${cem}`];
|
|
1301
|
+
if (!cls) {
|
|
1302
|
+
throw new Error(`decodeASTC4x4Block: unsupported CEM ${cem} with block mode 0x${mode.toString(16)}`);
|
|
1165
1303
|
}
|
|
1166
1304
|
const nVals = cls.channels.length * 2;
|
|
1167
1305
|
const v = [];
|
|
1168
|
-
|
|
1306
|
+
if (cls.endpointRange === 192) {
|
|
1307
|
+
let pos = 17;
|
|
1308
|
+
for (let g = 0; g < nVals; g += 5) {
|
|
1309
|
+
const n = Math.min(5, nVals - g);
|
|
1310
|
+
const bits = [];
|
|
1311
|
+
let T = 0;
|
|
1312
|
+
for (let i = 0; i < n; i++) {
|
|
1313
|
+
bits.push(br.read(pos, 6));
|
|
1314
|
+
pos += 6;
|
|
1315
|
+
const [start, count] = TRIT_SLICES[i];
|
|
1316
|
+
T |= br.read(pos, count) << start;
|
|
1317
|
+
pos += count;
|
|
1318
|
+
}
|
|
1319
|
+
const t = decodeTrits(T);
|
|
1320
|
+
for (let i = 0; i < n; i++) v.push(unq192(t[i] << 6 | bits[i]));
|
|
1321
|
+
}
|
|
1322
|
+
} else {
|
|
1323
|
+
for (let i = 0; i < nVals; i++) v.push(br.read(17 + i * 8, 8));
|
|
1324
|
+
}
|
|
1169
1325
|
let e0;
|
|
1170
1326
|
let e1;
|
|
1171
1327
|
if (cls.cem === CEM_LUM_DIRECT) {
|
|
@@ -1454,6 +1610,7 @@ var A3 = MODIFIERS.map((m) => 3 * m[0]);
|
|
|
1454
1610
|
var B3 = MODIFIERS.map((m) => 3 * m[1]);
|
|
1455
1611
|
var THR3 = MODIFIERS.map((m) => 1.5 * (m[0] + m[1]));
|
|
1456
1612
|
var PLANAR_FUDGE = 8;
|
|
1613
|
+
var KAPPA = 0.9;
|
|
1457
1614
|
function fastTableScore(D, t) {
|
|
1458
1615
|
let acc = 0;
|
|
1459
1616
|
for (let i = 0; i < D.length; i++) {
|
|
@@ -1543,13 +1700,14 @@ function encodeFastBlock(px) {
|
|
|
1543
1700
|
const residual = (s) => {
|
|
1544
1701
|
const dotSum = s.sum[0] * s.sum[0] + s.sum[1] * s.sum[1] + s.sum[2] * s.sum[2];
|
|
1545
1702
|
const lsum = s.sum[0] + s.sum[1] + s.sum[2];
|
|
1546
|
-
return s.sq - dotSum / 8 - (s.lsq - lsum * lsum / 8) / 3;
|
|
1703
|
+
return s.sq - dotSum / 8 - KAPPA * (s.lsq - lsum * lsum / 8) / 3;
|
|
1547
1704
|
};
|
|
1548
1705
|
const resA = residual(pair(0, 2)) + residual(pair(1, 3));
|
|
1549
1706
|
const resB = residual(pair(0, 1)) + residual(pair(2, 3));
|
|
1707
|
+
const gray = qsum.every((q) => q[0] === q[1] && q[1] === q[2]);
|
|
1550
1708
|
let flip;
|
|
1551
1709
|
let cur;
|
|
1552
|
-
if (
|
|
1710
|
+
if (gray) {
|
|
1553
1711
|
const fa = evalFlip(0, pair(0, 2), pair(1, 3));
|
|
1554
1712
|
const fb = evalFlip(1, pair(0, 1), pair(2, 3));
|
|
1555
1713
|
flip = fb.est < fa.est ? 1 : 0;
|