scax-engine 0.2.0-beta.1 → 0.2.0-beta.2

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.
@@ -7013,119 +7013,6 @@ class Ray {
7013
7013
  }
7014
7014
  }
7015
7015
 
7016
- /**
7017
- * 2D affine 왜곡 추정 전용 클래스입니다.
7018
- * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7019
- */
7020
- class Affine {
7021
- constructor() {
7022
- this.lastResult = null;
7023
- this.lastPairs = [];
7024
- }
7025
- estimate(pairs) {
7026
- const inputPairs = Array.isArray(pairs) ? pairs : [];
7027
- this.lastPairs = inputPairs;
7028
- const affine = this.fitAffine2D(inputPairs);
7029
- if (!affine) {
7030
- this.lastResult = null;
7031
- return null;
7032
- }
7033
- let residualSumPct = 0;
7034
- let residualCount = 0;
7035
- let residualMaxPct = 0;
7036
- const residuals = [];
7037
- for (const pair of inputPairs) {
7038
- const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7039
- const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7040
- const rx = pair.tx - px;
7041
- const ry = pair.ty - py;
7042
- const magnitude = Math.hypot(rx, ry);
7043
- if (magnitude < 1e-4)
7044
- continue;
7045
- const radiusRef = Math.hypot(px, py);
7046
- const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7047
- residualSumPct += pct;
7048
- residualCount += 1;
7049
- residualMaxPct = Math.max(residualMaxPct, pct);
7050
- residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7051
- }
7052
- const result = {
7053
- ...affine,
7054
- count: inputPairs.length,
7055
- residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7056
- residualMaxPct,
7057
- residuals,
7058
- };
7059
- this.lastResult = result;
7060
- return result;
7061
- }
7062
- /**
7063
- * 마지막 affine 추정 결과를 반환합니다.
7064
- */
7065
- getLastResult() {
7066
- return this.lastResult;
7067
- }
7068
- /**
7069
- * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7070
- */
7071
- getLastPairs() {
7072
- return [...this.lastPairs];
7073
- }
7074
- fitAffine2D(pairs) {
7075
- if (!Array.isArray(pairs) || pairs.length < 4)
7076
- return null;
7077
- const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7078
- const atb = Array(6).fill(0);
7079
- const accumulate = (row, rhs) => {
7080
- for (let i = 0; i < 6; i += 1) {
7081
- atb[i] += row[i] * rhs;
7082
- for (let j = 0; j < 6; j += 1)
7083
- ata[i][j] += row[i] * row[j];
7084
- }
7085
- };
7086
- for (const pair of pairs) {
7087
- accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7088
- accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7089
- }
7090
- const n = 6;
7091
- const aug = ata.map((row, index) => [...row, atb[index]]);
7092
- for (let col = 0; col < n; col += 1) {
7093
- let pivot = col;
7094
- for (let row = col + 1; row < n; row += 1) {
7095
- if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7096
- pivot = row;
7097
- }
7098
- if (Math.abs(aug[pivot][col]) < 1e-10)
7099
- return null;
7100
- if (pivot !== col) {
7101
- const temp = aug[col];
7102
- aug[col] = aug[pivot];
7103
- aug[pivot] = temp;
7104
- }
7105
- const divider = aug[col][col];
7106
- for (let j = col; j <= n; j += 1)
7107
- aug[col][j] /= divider;
7108
- for (let row = 0; row < n; row += 1) {
7109
- if (row === col)
7110
- continue;
7111
- const factor = aug[row][col];
7112
- if (Math.abs(factor) < 1e-12)
7113
- continue;
7114
- for (let j = col; j <= n; j += 1)
7115
- aug[row][j] -= factor * aug[col][j];
7116
- }
7117
- }
7118
- return {
7119
- a: aug[0][n],
7120
- b: aug[1][n],
7121
- c: aug[2][n],
7122
- d: aug[3][n],
7123
- e: aug[4][n],
7124
- f: aug[5][n],
7125
- };
7126
- }
7127
- }
7128
-
7129
7016
  class LightSource {
7130
7017
  constructor() {
7131
7018
  this.rays = [];
@@ -7337,6 +7224,156 @@ class RadialLightSource extends LightSource {
7337
7224
  }
7338
7225
  }
7339
7226
 
7227
+ class Surface {
7228
+ constructor(props) {
7229
+ this.type = "";
7230
+ this.name = "";
7231
+ this.position = new Vector3(0, 0, 0);
7232
+ this.tilt = new Vector2(0, 0);
7233
+ this.meridians = [];
7234
+ const { type, name, position, tilt } = props;
7235
+ this.type = type;
7236
+ this.name = name;
7237
+ this.position = new Vector3(position.x, position.y, position.z);
7238
+ this.tilt = new Vector2(tilt.x, tilt.y);
7239
+ this.incidentRays = [];
7240
+ this.refractedRays = [];
7241
+ }
7242
+ clearTraceHistory() {
7243
+ this.incidentRays = [];
7244
+ this.refractedRays = [];
7245
+ }
7246
+ getWorldPosition() {
7247
+ return this.position.clone();
7248
+ }
7249
+ setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7250
+ this.position.copy(position);
7251
+ this.tilt.set(tiltXDeg, tiltYDeg);
7252
+ }
7253
+ /**
7254
+ * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7255
+ */
7256
+ applyRigidRotationAboutPivot(pivot, rotation) {
7257
+ const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7258
+ const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7259
+ this.position.copy(p1);
7260
+ this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7261
+ }
7262
+ }
7263
+
7264
+ /**
7265
+ * 2D affine 왜곡 추정 전용 클래스입니다.
7266
+ * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7267
+ */
7268
+ class Affine {
7269
+ constructor() {
7270
+ this.lastResult = null;
7271
+ this.lastPairs = [];
7272
+ }
7273
+ estimate(pairs) {
7274
+ const inputPairs = Array.isArray(pairs) ? pairs : [];
7275
+ this.lastPairs = inputPairs;
7276
+ const affine = this.fitAffine2D(inputPairs);
7277
+ if (!affine) {
7278
+ this.lastResult = null;
7279
+ return null;
7280
+ }
7281
+ let residualSumPct = 0;
7282
+ let residualCount = 0;
7283
+ let residualMaxPct = 0;
7284
+ const residuals = [];
7285
+ for (const pair of inputPairs) {
7286
+ const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7287
+ const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7288
+ const rx = pair.tx - px;
7289
+ const ry = pair.ty - py;
7290
+ const magnitude = Math.hypot(rx, ry);
7291
+ if (magnitude < 1e-4)
7292
+ continue;
7293
+ const radiusRef = Math.hypot(px, py);
7294
+ const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7295
+ residualSumPct += pct;
7296
+ residualCount += 1;
7297
+ residualMaxPct = Math.max(residualMaxPct, pct);
7298
+ residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7299
+ }
7300
+ const result = {
7301
+ ...affine,
7302
+ count: inputPairs.length,
7303
+ residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7304
+ residualMaxPct,
7305
+ residuals,
7306
+ };
7307
+ this.lastResult = result;
7308
+ return result;
7309
+ }
7310
+ /**
7311
+ * 마지막 affine 추정 결과를 반환합니다.
7312
+ */
7313
+ getLastResult() {
7314
+ return this.lastResult;
7315
+ }
7316
+ /**
7317
+ * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7318
+ */
7319
+ getLastPairs() {
7320
+ return [...this.lastPairs];
7321
+ }
7322
+ fitAffine2D(pairs) {
7323
+ if (!Array.isArray(pairs) || pairs.length < 4)
7324
+ return null;
7325
+ const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7326
+ const atb = Array(6).fill(0);
7327
+ const accumulate = (row, rhs) => {
7328
+ for (let i = 0; i < 6; i += 1) {
7329
+ atb[i] += row[i] * rhs;
7330
+ for (let j = 0; j < 6; j += 1)
7331
+ ata[i][j] += row[i] * row[j];
7332
+ }
7333
+ };
7334
+ for (const pair of pairs) {
7335
+ accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7336
+ accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7337
+ }
7338
+ const n = 6;
7339
+ const aug = ata.map((row, index) => [...row, atb[index]]);
7340
+ for (let col = 0; col < n; col += 1) {
7341
+ let pivot = col;
7342
+ for (let row = col + 1; row < n; row += 1) {
7343
+ if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7344
+ pivot = row;
7345
+ }
7346
+ if (Math.abs(aug[pivot][col]) < 1e-10)
7347
+ return null;
7348
+ if (pivot !== col) {
7349
+ const temp = aug[col];
7350
+ aug[col] = aug[pivot];
7351
+ aug[pivot] = temp;
7352
+ }
7353
+ const divider = aug[col][col];
7354
+ for (let j = col; j <= n; j += 1)
7355
+ aug[col][j] /= divider;
7356
+ for (let row = 0; row < n; row += 1) {
7357
+ if (row === col)
7358
+ continue;
7359
+ const factor = aug[row][col];
7360
+ if (Math.abs(factor) < 1e-12)
7361
+ continue;
7362
+ for (let j = col; j <= n; j += 1)
7363
+ aug[row][j] -= factor * aug[col][j];
7364
+ }
7365
+ }
7366
+ return {
7367
+ a: aug[0][n],
7368
+ b: aug[1][n],
7369
+ c: aug[2][n],
7370
+ d: aug[3][n],
7371
+ e: aug[4][n],
7372
+ f: aug[5][n],
7373
+ };
7374
+ }
7375
+ }
7376
+
7340
7377
  /** 각막 기준 안구 회전점 z(mm). `SCAXEngineCore`와 동일. */
7341
7378
  const EYE_ROTATION_PIVOT_FROM_CORNEA_MM = 13;
7342
7379
  function normalizePrismAmount$2(value) {
@@ -7409,43 +7446,6 @@ function applyRigidEyePoseToSurfaces(surfaces, pivot, rotation) {
7409
7446
  }
7410
7447
  }
7411
7448
 
7412
- class Surface {
7413
- constructor(props) {
7414
- this.type = "";
7415
- this.name = "";
7416
- this.position = new Vector3(0, 0, 0);
7417
- this.tilt = new Vector2(0, 0);
7418
- this.meridians = [];
7419
- const { type, name, position, tilt } = props;
7420
- this.type = type;
7421
- this.name = name;
7422
- this.position = new Vector3(position.x, position.y, position.z);
7423
- this.tilt = new Vector2(tilt.x, tilt.y);
7424
- this.incidentRays = [];
7425
- this.refractedRays = [];
7426
- }
7427
- clearTraceHistory() {
7428
- this.incidentRays = [];
7429
- this.refractedRays = [];
7430
- }
7431
- getWorldPosition() {
7432
- return this.position.clone();
7433
- }
7434
- setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7435
- this.position.copy(position);
7436
- this.tilt.set(tiltXDeg, tiltYDeg);
7437
- }
7438
- /**
7439
- * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7440
- */
7441
- applyRigidRotationAboutPivot(pivot, rotation) {
7442
- const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7443
- const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7444
- this.position.copy(p1);
7445
- this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7446
- }
7447
- }
7448
-
7449
7449
  class ApertureStopSurface extends Surface {
7450
7450
  constructor(props) {
7451
7451
  super({
@@ -9641,7 +9641,9 @@ class SCAXEngine {
9641
9641
  }
9642
9642
  }
9643
9643
 
9644
+ exports.LightSource = LightSource;
9644
9645
  exports.Ray = Ray;
9645
9646
  exports.SCAXEngine = SCAXEngine;
9646
9647
  exports.SCAXEngineCore = SCAXEngineCore;
9648
+ exports.Surface = Surface;
9647
9649
  //# sourceMappingURL=scax-engine.cjs.map
@@ -7011,119 +7011,6 @@ class Ray {
7011
7011
  }
7012
7012
  }
7013
7013
 
7014
- /**
7015
- * 2D affine 왜곡 추정 전용 클래스입니다.
7016
- * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7017
- */
7018
- class Affine {
7019
- constructor() {
7020
- this.lastResult = null;
7021
- this.lastPairs = [];
7022
- }
7023
- estimate(pairs) {
7024
- const inputPairs = Array.isArray(pairs) ? pairs : [];
7025
- this.lastPairs = inputPairs;
7026
- const affine = this.fitAffine2D(inputPairs);
7027
- if (!affine) {
7028
- this.lastResult = null;
7029
- return null;
7030
- }
7031
- let residualSumPct = 0;
7032
- let residualCount = 0;
7033
- let residualMaxPct = 0;
7034
- const residuals = [];
7035
- for (const pair of inputPairs) {
7036
- const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7037
- const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7038
- const rx = pair.tx - px;
7039
- const ry = pair.ty - py;
7040
- const magnitude = Math.hypot(rx, ry);
7041
- if (magnitude < 1e-4)
7042
- continue;
7043
- const radiusRef = Math.hypot(px, py);
7044
- const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7045
- residualSumPct += pct;
7046
- residualCount += 1;
7047
- residualMaxPct = Math.max(residualMaxPct, pct);
7048
- residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7049
- }
7050
- const result = {
7051
- ...affine,
7052
- count: inputPairs.length,
7053
- residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7054
- residualMaxPct,
7055
- residuals,
7056
- };
7057
- this.lastResult = result;
7058
- return result;
7059
- }
7060
- /**
7061
- * 마지막 affine 추정 결과를 반환합니다.
7062
- */
7063
- getLastResult() {
7064
- return this.lastResult;
7065
- }
7066
- /**
7067
- * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7068
- */
7069
- getLastPairs() {
7070
- return [...this.lastPairs];
7071
- }
7072
- fitAffine2D(pairs) {
7073
- if (!Array.isArray(pairs) || pairs.length < 4)
7074
- return null;
7075
- const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7076
- const atb = Array(6).fill(0);
7077
- const accumulate = (row, rhs) => {
7078
- for (let i = 0; i < 6; i += 1) {
7079
- atb[i] += row[i] * rhs;
7080
- for (let j = 0; j < 6; j += 1)
7081
- ata[i][j] += row[i] * row[j];
7082
- }
7083
- };
7084
- for (const pair of pairs) {
7085
- accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7086
- accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7087
- }
7088
- const n = 6;
7089
- const aug = ata.map((row, index) => [...row, atb[index]]);
7090
- for (let col = 0; col < n; col += 1) {
7091
- let pivot = col;
7092
- for (let row = col + 1; row < n; row += 1) {
7093
- if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7094
- pivot = row;
7095
- }
7096
- if (Math.abs(aug[pivot][col]) < 1e-10)
7097
- return null;
7098
- if (pivot !== col) {
7099
- const temp = aug[col];
7100
- aug[col] = aug[pivot];
7101
- aug[pivot] = temp;
7102
- }
7103
- const divider = aug[col][col];
7104
- for (let j = col; j <= n; j += 1)
7105
- aug[col][j] /= divider;
7106
- for (let row = 0; row < n; row += 1) {
7107
- if (row === col)
7108
- continue;
7109
- const factor = aug[row][col];
7110
- if (Math.abs(factor) < 1e-12)
7111
- continue;
7112
- for (let j = col; j <= n; j += 1)
7113
- aug[row][j] -= factor * aug[col][j];
7114
- }
7115
- }
7116
- return {
7117
- a: aug[0][n],
7118
- b: aug[1][n],
7119
- c: aug[2][n],
7120
- d: aug[3][n],
7121
- e: aug[4][n],
7122
- f: aug[5][n],
7123
- };
7124
- }
7125
- }
7126
-
7127
7014
  class LightSource {
7128
7015
  constructor() {
7129
7016
  this.rays = [];
@@ -7335,6 +7222,156 @@ class RadialLightSource extends LightSource {
7335
7222
  }
7336
7223
  }
7337
7224
 
7225
+ class Surface {
7226
+ constructor(props) {
7227
+ this.type = "";
7228
+ this.name = "";
7229
+ this.position = new Vector3(0, 0, 0);
7230
+ this.tilt = new Vector2(0, 0);
7231
+ this.meridians = [];
7232
+ const { type, name, position, tilt } = props;
7233
+ this.type = type;
7234
+ this.name = name;
7235
+ this.position = new Vector3(position.x, position.y, position.z);
7236
+ this.tilt = new Vector2(tilt.x, tilt.y);
7237
+ this.incidentRays = [];
7238
+ this.refractedRays = [];
7239
+ }
7240
+ clearTraceHistory() {
7241
+ this.incidentRays = [];
7242
+ this.refractedRays = [];
7243
+ }
7244
+ getWorldPosition() {
7245
+ return this.position.clone();
7246
+ }
7247
+ setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7248
+ this.position.copy(position);
7249
+ this.tilt.set(tiltXDeg, tiltYDeg);
7250
+ }
7251
+ /**
7252
+ * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7253
+ */
7254
+ applyRigidRotationAboutPivot(pivot, rotation) {
7255
+ const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7256
+ const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7257
+ this.position.copy(p1);
7258
+ this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7259
+ }
7260
+ }
7261
+
7262
+ /**
7263
+ * 2D affine 왜곡 추정 전용 클래스입니다.
7264
+ * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7265
+ */
7266
+ class Affine {
7267
+ constructor() {
7268
+ this.lastResult = null;
7269
+ this.lastPairs = [];
7270
+ }
7271
+ estimate(pairs) {
7272
+ const inputPairs = Array.isArray(pairs) ? pairs : [];
7273
+ this.lastPairs = inputPairs;
7274
+ const affine = this.fitAffine2D(inputPairs);
7275
+ if (!affine) {
7276
+ this.lastResult = null;
7277
+ return null;
7278
+ }
7279
+ let residualSumPct = 0;
7280
+ let residualCount = 0;
7281
+ let residualMaxPct = 0;
7282
+ const residuals = [];
7283
+ for (const pair of inputPairs) {
7284
+ const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7285
+ const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7286
+ const rx = pair.tx - px;
7287
+ const ry = pair.ty - py;
7288
+ const magnitude = Math.hypot(rx, ry);
7289
+ if (magnitude < 1e-4)
7290
+ continue;
7291
+ const radiusRef = Math.hypot(px, py);
7292
+ const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7293
+ residualSumPct += pct;
7294
+ residualCount += 1;
7295
+ residualMaxPct = Math.max(residualMaxPct, pct);
7296
+ residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7297
+ }
7298
+ const result = {
7299
+ ...affine,
7300
+ count: inputPairs.length,
7301
+ residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7302
+ residualMaxPct,
7303
+ residuals,
7304
+ };
7305
+ this.lastResult = result;
7306
+ return result;
7307
+ }
7308
+ /**
7309
+ * 마지막 affine 추정 결과를 반환합니다.
7310
+ */
7311
+ getLastResult() {
7312
+ return this.lastResult;
7313
+ }
7314
+ /**
7315
+ * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7316
+ */
7317
+ getLastPairs() {
7318
+ return [...this.lastPairs];
7319
+ }
7320
+ fitAffine2D(pairs) {
7321
+ if (!Array.isArray(pairs) || pairs.length < 4)
7322
+ return null;
7323
+ const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7324
+ const atb = Array(6).fill(0);
7325
+ const accumulate = (row, rhs) => {
7326
+ for (let i = 0; i < 6; i += 1) {
7327
+ atb[i] += row[i] * rhs;
7328
+ for (let j = 0; j < 6; j += 1)
7329
+ ata[i][j] += row[i] * row[j];
7330
+ }
7331
+ };
7332
+ for (const pair of pairs) {
7333
+ accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7334
+ accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7335
+ }
7336
+ const n = 6;
7337
+ const aug = ata.map((row, index) => [...row, atb[index]]);
7338
+ for (let col = 0; col < n; col += 1) {
7339
+ let pivot = col;
7340
+ for (let row = col + 1; row < n; row += 1) {
7341
+ if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7342
+ pivot = row;
7343
+ }
7344
+ if (Math.abs(aug[pivot][col]) < 1e-10)
7345
+ return null;
7346
+ if (pivot !== col) {
7347
+ const temp = aug[col];
7348
+ aug[col] = aug[pivot];
7349
+ aug[pivot] = temp;
7350
+ }
7351
+ const divider = aug[col][col];
7352
+ for (let j = col; j <= n; j += 1)
7353
+ aug[col][j] /= divider;
7354
+ for (let row = 0; row < n; row += 1) {
7355
+ if (row === col)
7356
+ continue;
7357
+ const factor = aug[row][col];
7358
+ if (Math.abs(factor) < 1e-12)
7359
+ continue;
7360
+ for (let j = col; j <= n; j += 1)
7361
+ aug[row][j] -= factor * aug[col][j];
7362
+ }
7363
+ }
7364
+ return {
7365
+ a: aug[0][n],
7366
+ b: aug[1][n],
7367
+ c: aug[2][n],
7368
+ d: aug[3][n],
7369
+ e: aug[4][n],
7370
+ f: aug[5][n],
7371
+ };
7372
+ }
7373
+ }
7374
+
7338
7375
  /** 각막 기준 안구 회전점 z(mm). `SCAXEngineCore`와 동일. */
7339
7376
  const EYE_ROTATION_PIVOT_FROM_CORNEA_MM = 13;
7340
7377
  function normalizePrismAmount$2(value) {
@@ -7407,43 +7444,6 @@ function applyRigidEyePoseToSurfaces(surfaces, pivot, rotation) {
7407
7444
  }
7408
7445
  }
7409
7446
 
7410
- class Surface {
7411
- constructor(props) {
7412
- this.type = "";
7413
- this.name = "";
7414
- this.position = new Vector3(0, 0, 0);
7415
- this.tilt = new Vector2(0, 0);
7416
- this.meridians = [];
7417
- const { type, name, position, tilt } = props;
7418
- this.type = type;
7419
- this.name = name;
7420
- this.position = new Vector3(position.x, position.y, position.z);
7421
- this.tilt = new Vector2(tilt.x, tilt.y);
7422
- this.incidentRays = [];
7423
- this.refractedRays = [];
7424
- }
7425
- clearTraceHistory() {
7426
- this.incidentRays = [];
7427
- this.refractedRays = [];
7428
- }
7429
- getWorldPosition() {
7430
- return this.position.clone();
7431
- }
7432
- setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7433
- this.position.copy(position);
7434
- this.tilt.set(tiltXDeg, tiltYDeg);
7435
- }
7436
- /**
7437
- * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7438
- */
7439
- applyRigidRotationAboutPivot(pivot, rotation) {
7440
- const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7441
- const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7442
- this.position.copy(p1);
7443
- this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7444
- }
7445
- }
7446
-
7447
7447
  class ApertureStopSurface extends Surface {
7448
7448
  constructor(props) {
7449
7449
  super({
@@ -9639,5 +9639,5 @@ class SCAXEngine {
9639
9639
  }
9640
9640
  }
9641
9641
 
9642
- export { Ray, SCAXEngine, SCAXEngineCore };
9642
+ export { LightSource, Ray, SCAXEngine, SCAXEngineCore, Surface };
9643
9643
  //# sourceMappingURL=scax-engine.js.map
@@ -7017,119 +7017,6 @@
7017
7017
  }
7018
7018
  }
7019
7019
 
7020
- /**
7021
- * 2D affine 왜곡 추정 전용 클래스입니다.
7022
- * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7023
- */
7024
- class Affine {
7025
- constructor() {
7026
- this.lastResult = null;
7027
- this.lastPairs = [];
7028
- }
7029
- estimate(pairs) {
7030
- const inputPairs = Array.isArray(pairs) ? pairs : [];
7031
- this.lastPairs = inputPairs;
7032
- const affine = this.fitAffine2D(inputPairs);
7033
- if (!affine) {
7034
- this.lastResult = null;
7035
- return null;
7036
- }
7037
- let residualSumPct = 0;
7038
- let residualCount = 0;
7039
- let residualMaxPct = 0;
7040
- const residuals = [];
7041
- for (const pair of inputPairs) {
7042
- const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7043
- const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7044
- const rx = pair.tx - px;
7045
- const ry = pair.ty - py;
7046
- const magnitude = Math.hypot(rx, ry);
7047
- if (magnitude < 1e-4)
7048
- continue;
7049
- const radiusRef = Math.hypot(px, py);
7050
- const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7051
- residualSumPct += pct;
7052
- residualCount += 1;
7053
- residualMaxPct = Math.max(residualMaxPct, pct);
7054
- residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7055
- }
7056
- const result = {
7057
- ...affine,
7058
- count: inputPairs.length,
7059
- residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7060
- residualMaxPct,
7061
- residuals,
7062
- };
7063
- this.lastResult = result;
7064
- return result;
7065
- }
7066
- /**
7067
- * 마지막 affine 추정 결과를 반환합니다.
7068
- */
7069
- getLastResult() {
7070
- return this.lastResult;
7071
- }
7072
- /**
7073
- * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7074
- */
7075
- getLastPairs() {
7076
- return [...this.lastPairs];
7077
- }
7078
- fitAffine2D(pairs) {
7079
- if (!Array.isArray(pairs) || pairs.length < 4)
7080
- return null;
7081
- const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7082
- const atb = Array(6).fill(0);
7083
- const accumulate = (row, rhs) => {
7084
- for (let i = 0; i < 6; i += 1) {
7085
- atb[i] += row[i] * rhs;
7086
- for (let j = 0; j < 6; j += 1)
7087
- ata[i][j] += row[i] * row[j];
7088
- }
7089
- };
7090
- for (const pair of pairs) {
7091
- accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7092
- accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7093
- }
7094
- const n = 6;
7095
- const aug = ata.map((row, index) => [...row, atb[index]]);
7096
- for (let col = 0; col < n; col += 1) {
7097
- let pivot = col;
7098
- for (let row = col + 1; row < n; row += 1) {
7099
- if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7100
- pivot = row;
7101
- }
7102
- if (Math.abs(aug[pivot][col]) < 1e-10)
7103
- return null;
7104
- if (pivot !== col) {
7105
- const temp = aug[col];
7106
- aug[col] = aug[pivot];
7107
- aug[pivot] = temp;
7108
- }
7109
- const divider = aug[col][col];
7110
- for (let j = col; j <= n; j += 1)
7111
- aug[col][j] /= divider;
7112
- for (let row = 0; row < n; row += 1) {
7113
- if (row === col)
7114
- continue;
7115
- const factor = aug[row][col];
7116
- if (Math.abs(factor) < 1e-12)
7117
- continue;
7118
- for (let j = col; j <= n; j += 1)
7119
- aug[row][j] -= factor * aug[col][j];
7120
- }
7121
- }
7122
- return {
7123
- a: aug[0][n],
7124
- b: aug[1][n],
7125
- c: aug[2][n],
7126
- d: aug[3][n],
7127
- e: aug[4][n],
7128
- f: aug[5][n],
7129
- };
7130
- }
7131
- }
7132
-
7133
7020
  class LightSource {
7134
7021
  constructor() {
7135
7022
  this.rays = [];
@@ -7341,6 +7228,156 @@
7341
7228
  }
7342
7229
  }
7343
7230
 
7231
+ class Surface {
7232
+ constructor(props) {
7233
+ this.type = "";
7234
+ this.name = "";
7235
+ this.position = new Vector3(0, 0, 0);
7236
+ this.tilt = new Vector2(0, 0);
7237
+ this.meridians = [];
7238
+ const { type, name, position, tilt } = props;
7239
+ this.type = type;
7240
+ this.name = name;
7241
+ this.position = new Vector3(position.x, position.y, position.z);
7242
+ this.tilt = new Vector2(tilt.x, tilt.y);
7243
+ this.incidentRays = [];
7244
+ this.refractedRays = [];
7245
+ }
7246
+ clearTraceHistory() {
7247
+ this.incidentRays = [];
7248
+ this.refractedRays = [];
7249
+ }
7250
+ getWorldPosition() {
7251
+ return this.position.clone();
7252
+ }
7253
+ setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7254
+ this.position.copy(position);
7255
+ this.tilt.set(tiltXDeg, tiltYDeg);
7256
+ }
7257
+ /**
7258
+ * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7259
+ */
7260
+ applyRigidRotationAboutPivot(pivot, rotation) {
7261
+ const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7262
+ const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7263
+ this.position.copy(p1);
7264
+ this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7265
+ }
7266
+ }
7267
+
7268
+ /**
7269
+ * 2D affine 왜곡 추정 전용 클래스입니다.
7270
+ * [x'; y'] = [[a b c], [d e f]] * [x y 1]^T 형태를 최소자승으로 적합합니다.
7271
+ */
7272
+ class Affine {
7273
+ constructor() {
7274
+ this.lastResult = null;
7275
+ this.lastPairs = [];
7276
+ }
7277
+ estimate(pairs) {
7278
+ const inputPairs = Array.isArray(pairs) ? pairs : [];
7279
+ this.lastPairs = inputPairs;
7280
+ const affine = this.fitAffine2D(inputPairs);
7281
+ if (!affine) {
7282
+ this.lastResult = null;
7283
+ return null;
7284
+ }
7285
+ let residualSumPct = 0;
7286
+ let residualCount = 0;
7287
+ let residualMaxPct = 0;
7288
+ const residuals = [];
7289
+ for (const pair of inputPairs) {
7290
+ const px = affine.a * pair.sx + affine.b * pair.sy + affine.c;
7291
+ const py = affine.d * pair.sx + affine.e * pair.sy + affine.f;
7292
+ const rx = pair.tx - px;
7293
+ const ry = pair.ty - py;
7294
+ const magnitude = Math.hypot(rx, ry);
7295
+ if (magnitude < 1e-4)
7296
+ continue;
7297
+ const radiusRef = Math.hypot(px, py);
7298
+ const pct = (magnitude / Math.max(0.2, radiusRef)) * 100;
7299
+ residualSumPct += pct;
7300
+ residualCount += 1;
7301
+ residualMaxPct = Math.max(residualMaxPct, pct);
7302
+ residuals.push({ sx: pair.sx, sy: pair.sy, px, py, rx, ry, magnitude, pct });
7303
+ }
7304
+ const result = {
7305
+ ...affine,
7306
+ count: inputPairs.length,
7307
+ residualAvgPct: residualCount ? residualSumPct / residualCount : 0,
7308
+ residualMaxPct,
7309
+ residuals,
7310
+ };
7311
+ this.lastResult = result;
7312
+ return result;
7313
+ }
7314
+ /**
7315
+ * 마지막 affine 추정 결과를 반환합니다.
7316
+ */
7317
+ getLastResult() {
7318
+ return this.lastResult;
7319
+ }
7320
+ /**
7321
+ * 마지막 affine 추정에 사용된 입력쌍을 반환합니다.
7322
+ */
7323
+ getLastPairs() {
7324
+ return [...this.lastPairs];
7325
+ }
7326
+ fitAffine2D(pairs) {
7327
+ if (!Array.isArray(pairs) || pairs.length < 4)
7328
+ return null;
7329
+ const ata = Array.from({ length: 6 }, () => Array(6).fill(0));
7330
+ const atb = Array(6).fill(0);
7331
+ const accumulate = (row, rhs) => {
7332
+ for (let i = 0; i < 6; i += 1) {
7333
+ atb[i] += row[i] * rhs;
7334
+ for (let j = 0; j < 6; j += 1)
7335
+ ata[i][j] += row[i] * row[j];
7336
+ }
7337
+ };
7338
+ for (const pair of pairs) {
7339
+ accumulate([pair.sx, pair.sy, 1, 0, 0, 0], pair.tx);
7340
+ accumulate([0, 0, 0, pair.sx, pair.sy, 1], pair.ty);
7341
+ }
7342
+ const n = 6;
7343
+ const aug = ata.map((row, index) => [...row, atb[index]]);
7344
+ for (let col = 0; col < n; col += 1) {
7345
+ let pivot = col;
7346
+ for (let row = col + 1; row < n; row += 1) {
7347
+ if (Math.abs(aug[row][col]) > Math.abs(aug[pivot][col]))
7348
+ pivot = row;
7349
+ }
7350
+ if (Math.abs(aug[pivot][col]) < 1e-10)
7351
+ return null;
7352
+ if (pivot !== col) {
7353
+ const temp = aug[col];
7354
+ aug[col] = aug[pivot];
7355
+ aug[pivot] = temp;
7356
+ }
7357
+ const divider = aug[col][col];
7358
+ for (let j = col; j <= n; j += 1)
7359
+ aug[col][j] /= divider;
7360
+ for (let row = 0; row < n; row += 1) {
7361
+ if (row === col)
7362
+ continue;
7363
+ const factor = aug[row][col];
7364
+ if (Math.abs(factor) < 1e-12)
7365
+ continue;
7366
+ for (let j = col; j <= n; j += 1)
7367
+ aug[row][j] -= factor * aug[col][j];
7368
+ }
7369
+ }
7370
+ return {
7371
+ a: aug[0][n],
7372
+ b: aug[1][n],
7373
+ c: aug[2][n],
7374
+ d: aug[3][n],
7375
+ e: aug[4][n],
7376
+ f: aug[5][n],
7377
+ };
7378
+ }
7379
+ }
7380
+
7344
7381
  /** 각막 기준 안구 회전점 z(mm). `SCAXEngineCore`와 동일. */
7345
7382
  const EYE_ROTATION_PIVOT_FROM_CORNEA_MM = 13;
7346
7383
  function normalizePrismAmount$2(value) {
@@ -7413,43 +7450,6 @@
7413
7450
  }
7414
7451
  }
7415
7452
 
7416
- class Surface {
7417
- constructor(props) {
7418
- this.type = "";
7419
- this.name = "";
7420
- this.position = new Vector3(0, 0, 0);
7421
- this.tilt = new Vector2(0, 0);
7422
- this.meridians = [];
7423
- const { type, name, position, tilt } = props;
7424
- this.type = type;
7425
- this.name = name;
7426
- this.position = new Vector3(position.x, position.y, position.z);
7427
- this.tilt = new Vector2(tilt.x, tilt.y);
7428
- this.incidentRays = [];
7429
- this.refractedRays = [];
7430
- }
7431
- clearTraceHistory() {
7432
- this.incidentRays = [];
7433
- this.refractedRays = [];
7434
- }
7435
- getWorldPosition() {
7436
- return this.position.clone();
7437
- }
7438
- setPositionAndTilt(position, tiltXDeg, tiltYDeg) {
7439
- this.position.copy(position);
7440
- this.tilt.set(tiltXDeg, tiltYDeg);
7441
- }
7442
- /**
7443
- * 안질 tilt가 0인 표면을 전제로, pivot 기준 강체 회전 후 동일 Euler(XYZ) tilt를 부여합니다.
7444
- */
7445
- applyRigidRotationAboutPivot(pivot, rotation) {
7446
- const p1 = pivot.clone().add(new Vector3().subVectors(this.position, pivot).applyQuaternion(rotation));
7447
- const euler = new Euler().setFromQuaternion(rotation, "XYZ");
7448
- this.position.copy(p1);
7449
- this.tilt.set((euler.x * 180) / Math.PI, (euler.y * 180) / Math.PI);
7450
- }
7451
- }
7452
-
7453
7453
  class ApertureStopSurface extends Surface {
7454
7454
  constructor(props) {
7455
7455
  super({
@@ -9645,9 +9645,11 @@
9645
9645
  }
9646
9646
  }
9647
9647
 
9648
+ exports.LightSource = LightSource;
9648
9649
  exports.Ray = Ray;
9649
9650
  exports.SCAXEngine = SCAXEngine;
9650
9651
  exports.SCAXEngineCore = SCAXEngineCore;
9652
+ exports.Surface = Surface;
9651
9653
 
9652
9654
  }));
9653
9655
  //# sourceMappingURL=scax-engine.umd.js.map
@@ -1,4 +1,6 @@
1
1
  export { default as Ray } from "./ray/ray";
2
+ export { LightSource } from "./light-sources/light-source";
3
+ export { default as Surface } from "./surfaces/surface";
2
4
  export { default as SCAXEngine, SCAXEngineCore } from "./scax-engine";
3
- export type { MeridianInfo, PrismPower, SCAXEngineProps, SCAXPower, SimulateResult, } from "./scax-engine";
5
+ export type { MeridianInfo, PrismPower, SCAXEngineProps, SCAXPower, SimulateResult, SturmResult } from "./scax-engine";
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -2,6 +2,7 @@ import Affine from "./affine/affine";
2
2
  import { GridLightSourceProps, GridRGLightSourceProps, LightSource, RadialLightSourceProps } from "./light-sources/light-source";
3
3
  import { EyeModelParameter } from "./parameters/eye/eyemodel-parameter";
4
4
  import Ray from "./ray/ray";
5
+ import Sturm from "./sturm/sturm";
5
6
  import Surface from "./surfaces/surface";
6
7
  export type EyeModel = "gullstrand" | "navarro";
7
8
  export type PupilType = "constricted" | "neutral" | "dilated" | "none";
@@ -95,6 +96,8 @@ export type MeridianInfo = {
95
96
  tabo: number;
96
97
  d: number;
97
98
  }[];
99
+ /** `Sturm#calculate()` / `SCAXEngine#sturmCalculation()` 반환 구조 */
100
+ export type SturmResult = ReturnType<Sturm["calculate"]>;
98
101
  export type AffineAnalysisResult = ReturnType<Affine["estimate"]>;
99
102
  /**
100
103
  * legacy simulator.js를 TypeScript로 옮긴 핵심 시뮬레이터입니다.
@@ -143,117 +146,7 @@ export declare class SCAXEngineCore {
143
146
  * 2) Sturm calculation 전용 함수
144
147
  * traced ray 집합에서 z-scan 기반 Sturm 슬라이스/근사 중심을 계산합니다.
145
148
  */
146
- sturmCalculation(rays?: Ray[]): {
147
- slices_info: {
148
- count: number;
149
- slices: {
150
- z: number;
151
- depth: number;
152
- ratio: number;
153
- size: number;
154
- profile: {
155
- at: {
156
- x: number;
157
- y: number;
158
- z: number;
159
- };
160
- wMajor: number;
161
- wMinor: number;
162
- angleMajorDeg: number;
163
- j0: number;
164
- j45: number;
165
- angleMinorDeg: number;
166
- majorDirection: {
167
- x: number;
168
- y: number;
169
- z: number;
170
- };
171
- minorDirection: {
172
- x: number;
173
- y: number;
174
- z: number;
175
- };
176
- };
177
- }[];
178
- };
179
- sturm_info: {
180
- has_astigmatism: boolean;
181
- method: string;
182
- anterior: {
183
- z: number;
184
- depth: number;
185
- ratio: number;
186
- size: number;
187
- profile: {
188
- at: {
189
- x: number;
190
- y: number;
191
- z: number;
192
- };
193
- wMajor: number;
194
- wMinor: number;
195
- angleMajorDeg: number;
196
- j0: number;
197
- j45: number;
198
- angleMinorDeg: number;
199
- majorDirection: {
200
- x: number;
201
- y: number;
202
- z: number;
203
- };
204
- minorDirection: {
205
- x: number;
206
- y: number;
207
- z: number;
208
- };
209
- };
210
- };
211
- posterior: {
212
- z: number;
213
- depth: number;
214
- ratio: number;
215
- size: number;
216
- profile: {
217
- at: {
218
- x: number;
219
- y: number;
220
- z: number;
221
- };
222
- wMajor: number;
223
- wMinor: number;
224
- angleMajorDeg: number;
225
- j0: number;
226
- j45: number;
227
- angleMinorDeg: number;
228
- majorDirection: {
229
- x: number;
230
- y: number;
231
- z: number;
232
- };
233
- minorDirection: {
234
- x: number;
235
- y: number;
236
- z: number;
237
- };
238
- };
239
- } | null;
240
- approx_center: {
241
- x: number;
242
- y: number;
243
- z: number;
244
- mode: string;
245
- } | null;
246
- line: "g" | "F" | "e" | "d" | "C" | "r";
247
- wavelength_nm: number;
248
- color: number | null;
249
- ray_count: number;
250
- analysis_axis: {
251
- x: number;
252
- y: number;
253
- z: number;
254
- };
255
- }[];
256
- };
149
+ sturmCalculation(rays?: Ray[]): SturmResult;
257
150
  getAffineAnalysis(): AffineAnalysisResult;
258
151
  private surfaceOrderZ;
259
152
  private readSurfacePosition;
@@ -279,117 +172,7 @@ export default class SCAXEngine {
279
172
  dispose(): void;
280
173
  simulate(): SimulateResult;
281
174
  rayTracing(): Ray[];
282
- sturmCalculation(rays?: Ray[]): {
283
- slices_info: {
284
- count: number;
285
- slices: {
286
- z: number;
287
- depth: number;
288
- ratio: number;
289
- size: number;
290
- profile: {
291
- at: {
292
- x: number;
293
- y: number;
294
- z: number;
295
- };
296
- wMajor: number;
297
- wMinor: number;
298
- angleMajorDeg: number;
299
- j0: number;
300
- j45: number;
301
- angleMinorDeg: number;
302
- majorDirection: {
303
- x: number;
304
- y: number;
305
- z: number;
306
- };
307
- minorDirection: {
308
- x: number;
309
- y: number;
310
- z: number;
311
- };
312
- };
313
- }[];
314
- };
315
- sturm_info: {
316
- has_astigmatism: boolean;
317
- method: string;
318
- anterior: {
319
- z: number;
320
- depth: number;
321
- ratio: number;
322
- size: number;
323
- profile: {
324
- at: {
325
- x: number;
326
- y: number;
327
- z: number;
328
- };
329
- wMajor: number;
330
- wMinor: number;
331
- angleMajorDeg: number;
332
- j0: number;
333
- j45: number;
334
- angleMinorDeg: number;
335
- majorDirection: {
336
- x: number;
337
- y: number;
338
- z: number;
339
- };
340
- minorDirection: {
341
- x: number;
342
- y: number;
343
- z: number;
344
- };
345
- };
346
- };
347
- posterior: {
348
- z: number;
349
- depth: number;
350
- ratio: number;
351
- size: number;
352
- profile: {
353
- at: {
354
- x: number;
355
- y: number;
356
- z: number;
357
- };
358
- wMajor: number;
359
- wMinor: number;
360
- angleMajorDeg: number;
361
- j0: number;
362
- j45: number;
363
- angleMinorDeg: number;
364
- majorDirection: {
365
- x: number;
366
- y: number;
367
- z: number;
368
- };
369
- minorDirection: {
370
- x: number;
371
- y: number;
372
- z: number;
373
- };
374
- };
375
- } | null;
376
- approx_center: {
377
- x: number;
378
- y: number;
379
- z: number;
380
- mode: string;
381
- } | null;
382
- line: "g" | "F" | "e" | "d" | "C" | "r";
383
- wavelength_nm: number;
384
- color: number | null;
385
- ray_count: number;
386
- analysis_axis: {
387
- x: number;
388
- y: number;
389
- z: number;
390
- };
391
- }[];
392
- };
175
+ sturmCalculation(rays?: Ray[]): SturmResult;
393
176
  calculateMeridians(scaxPowers: SCAXPower[]): MeridianInfo;
394
177
  calculateEyeRotationByPrism(prism: PrismPower): {
395
178
  x: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scax-engine",
3
- "version": "0.2.0-beta.1",
3
+ "version": "0.2.0-beta.2",
4
4
  "description": "Optical calculation engine for an eye model, with ESM, CJS, and UMD builds.",
5
5
  "main": "./dist/scax-engine.cjs",
6
6
  "module": "./dist/scax-engine.js",