scax-engine 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -2,8 +2,14 @@
2
2
 
3
3
  눈 모델(Gullstrand / Navarro)에 대한 광선 추적, Sturm 간격 분석, 아핀 왜곡 추정, 유발 난시·프리즘 편위 계산을 제공하는 TypeScript 안경광학 시뮬레이션(단안 기준, OD) 라이브러리입니다. ESM, CJS, UMD 빌드를 지원합니다.
4
4
 
5
+
5
6
  English documentation: [README-en.md](README-en.md)
6
7
 
8
+ ## SCAX란?
9
+
10
+ scax는 안경광학에서 기본적으로 쓰이는 데이터입니다. **S**는 구면렌즈 도수, **C**는 원주렌즈 도수, **AX**는 원주렌즈의 축입니다.
11
+
12
+
7
13
  ## 요구 사항
8
14
 
9
15
  - **Node.js** 20 이상(로컬 개발·테스트)
@@ -6932,6 +6932,13 @@ const FRAUNHOFER_REFRACTIVE_INDICES = {
6932
6932
  d: 1.406,
6933
6933
  C: 1.405318,
6934
6934
  },
6935
+ // Navarro 수정체 중심 굴절률(d=1.42)용 분산 스펙
6936
+ lens_navarro: {
6937
+ F: 1.421585,
6938
+ e: 1.420542,
6939
+ d: 1.420,
6940
+ C: 1.419318,
6941
+ },
6935
6942
  lens_anterior: {
6936
6943
  F: 1.387507,
6937
6944
  e: 1.386516,
@@ -7885,48 +7892,48 @@ GullstrandParameter.parameter = {
7885
7892
  name: "cornea_anterior",
7886
7893
  z: 0.0,
7887
7894
  radius: 7.7,
7888
- n_before: 1.0,
7889
- n_after: 1.376,
7895
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7896
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7890
7897
  },
7891
7898
  {
7892
7899
  type: "spherical",
7893
7900
  name: "cornea_posterior",
7894
7901
  z: 0.5,
7895
7902
  radius: 6.8,
7896
- n_before: 1.376,
7897
- n_after: 1.336,
7903
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7904
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7898
7905
  },
7899
7906
  {
7900
7907
  type: "spherical",
7901
7908
  name: "lens_anterior",
7902
7909
  z: 3.6,
7903
7910
  radius: 10.0,
7904
- n_before: 1.336,
7905
- n_after: 1.386,
7911
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7912
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7906
7913
  },
7907
7914
  {
7908
7915
  type: "spherical",
7909
7916
  name: "lens_nucleus_anterior",
7910
7917
  z: 4.146,
7911
7918
  radius: 7.911,
7912
- n_before: 1.386,
7913
- n_after: 1.406,
7919
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7920
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7914
7921
  },
7915
7922
  {
7916
7923
  type: "spherical",
7917
7924
  name: "lens_nucleus_posterior",
7918
7925
  z: 6.565,
7919
7926
  radius: -5.76,
7920
- n_before: 1.406,
7921
- n_after: 1.386,
7927
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7928
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7922
7929
  },
7923
7930
  {
7924
7931
  type: "spherical",
7925
7932
  name: "lens_posterior",
7926
7933
  z: 7.2,
7927
7934
  radius: -6,
7928
- n_before: 1.386,
7929
- n_after: 1.336,
7935
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7936
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7930
7937
  },
7931
7938
  {
7932
7939
  type: "spherical-image",
@@ -7952,8 +7959,8 @@ NavarroParameter.parameter = {
7952
7959
  z: 0.0,
7953
7960
  radius: 7.72,
7954
7961
  conic: -0.26,
7955
- n_before: 1.0,
7956
- n_after: 1.376,
7962
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7963
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7957
7964
  },
7958
7965
  {
7959
7966
  type: "aspherical",
@@ -7961,8 +7968,8 @@ NavarroParameter.parameter = {
7961
7968
  z: 0.55,
7962
7969
  radius: 6.5,
7963
7970
  conic: 0.0,
7964
- n_before: 1.376,
7965
- n_after: 1.336,
7971
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7972
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7966
7973
  },
7967
7974
  {
7968
7975
  type: "aspherical",
@@ -7970,8 +7977,8 @@ NavarroParameter.parameter = {
7970
7977
  z: 0.55 + 3.05,
7971
7978
  radius: 10.2,
7972
7979
  conic: -3.13,
7973
- n_before: 1.336,
7974
- n_after: 1.42,
7980
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7981
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7975
7982
  },
7976
7983
  {
7977
7984
  type: "aspherical",
@@ -7979,8 +7986,8 @@ NavarroParameter.parameter = {
7979
7986
  z: 0.55 + 3.05 + 4.0,
7980
7987
  radius: -6,
7981
7988
  conic: -1,
7982
- n_before: 1.42,
7983
- n_after: 1.336,
7989
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7990
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7984
7991
  },
7985
7992
  {
7986
7993
  type: "spherical-image",
@@ -8339,6 +8346,13 @@ class ApertureStopSurface extends Surface {
8339
8346
  }
8340
8347
  }
8341
8348
 
8349
+ const DegToTABO = (degree) => {
8350
+ const d = Number(degree);
8351
+ if (!Number.isFinite(d))
8352
+ return 0;
8353
+ return (((180 - d) % 180) + 180) % 180;
8354
+ };
8355
+
8342
8356
  class ToricSurface extends Surface {
8343
8357
  constructor(props) {
8344
8358
  super({ type: "toric", name: props.name, position: props.position, tilt: props.tilt });
@@ -8554,7 +8568,8 @@ class STSurface extends Surface {
8554
8568
  const { s, c, ax, n_before = 1.0, n = 1.0, n_after = n_before, referencePoint, thickness = ST_DEFAULT_THICKNESS_MM, } = props;
8555
8569
  this.s = s;
8556
8570
  this.c = c;
8557
- this.ax = ax;
8571
+ // paraxial-surface와 동일: 처방 축은 TABO(°), 굴절/기하에는 수학 좌표 각(0~180)로 변환해 저장한다.
8572
+ this.ax = DegToTABO(ax);
8558
8573
  this.n_before = normalizeRefractiveIndexSpec(n_before);
8559
8574
  this.n = normalizeRefractiveIndexSpec(n);
8560
8575
  this.n_after = normalizeRefractiveIndexSpec(n_after);
@@ -8803,13 +8818,6 @@ const TABOToDeg = (TABOAngle) => {
8803
8818
  return (((180 - t) % 180) + 180) % 180;
8804
8819
  };
8805
8820
 
8806
- const DegToTABO = (degree) => {
8807
- const d = Number(degree);
8808
- if (!Number.isFinite(d))
8809
- return 0;
8810
- return (((180 - d) % 180) + 180) % 180;
8811
- };
8812
-
8813
8821
  /**
8814
8822
  * legacy simulator.js를 TypeScript로 옮긴 핵심 시뮬레이터입니다.
8815
8823
  * - 광원 광선을 생성하고
@@ -9177,12 +9185,19 @@ class SCAXEngineCore {
9177
9185
  }
9178
9186
  return { m, j0, j45 };
9179
9187
  }
9188
+ /** 난시 주경선 TABO 각도: 180° 동치이므로 표시·비교는 항상 [0, 180)으로 맞춘다. */
9189
+ normalizeTaboMeridian180(value) {
9190
+ const d = Number(value ?? 0);
9191
+ if (!Number.isFinite(d))
9192
+ return 0;
9193
+ return ((d % 180) + 180) % 180;
9194
+ }
9180
9195
  principalMeridiansFromVector(m, j0, j45) {
9181
9196
  if (!Number.isFinite(m) || !Number.isFinite(j0) || !Number.isFinite(j45))
9182
9197
  return [];
9183
9198
  const axisDeg = (((0.5 * Math.atan2(j45, j0) * 180) / Math.PI) % 180 + 180) % 180;
9184
- const taboAxis = this.normalizeAngle360(TABOToDeg(axisDeg));
9185
- const orthogonalTabo = this.normalizeAngle360(taboAxis + 90);
9199
+ const taboAxis = this.normalizeTaboMeridian180(TABOToDeg(axisDeg));
9200
+ const orthogonalTabo = (taboAxis + 90) % 180;
9186
9201
  const r = Math.hypot(j0, j45);
9187
9202
  const meridians = [
9188
9203
  { tabo: taboAxis, d: m - r },
@@ -6930,6 +6930,13 @@ const FRAUNHOFER_REFRACTIVE_INDICES = {
6930
6930
  d: 1.406,
6931
6931
  C: 1.405318,
6932
6932
  },
6933
+ // Navarro 수정체 중심 굴절률(d=1.42)용 분산 스펙
6934
+ lens_navarro: {
6935
+ F: 1.421585,
6936
+ e: 1.420542,
6937
+ d: 1.420,
6938
+ C: 1.419318,
6939
+ },
6933
6940
  lens_anterior: {
6934
6941
  F: 1.387507,
6935
6942
  e: 1.386516,
@@ -7883,48 +7890,48 @@ GullstrandParameter.parameter = {
7883
7890
  name: "cornea_anterior",
7884
7891
  z: 0.0,
7885
7892
  radius: 7.7,
7886
- n_before: 1.0,
7887
- n_after: 1.376,
7893
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7894
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7888
7895
  },
7889
7896
  {
7890
7897
  type: "spherical",
7891
7898
  name: "cornea_posterior",
7892
7899
  z: 0.5,
7893
7900
  radius: 6.8,
7894
- n_before: 1.376,
7895
- n_after: 1.336,
7901
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7902
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7896
7903
  },
7897
7904
  {
7898
7905
  type: "spherical",
7899
7906
  name: "lens_anterior",
7900
7907
  z: 3.6,
7901
7908
  radius: 10.0,
7902
- n_before: 1.336,
7903
- n_after: 1.386,
7909
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7910
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7904
7911
  },
7905
7912
  {
7906
7913
  type: "spherical",
7907
7914
  name: "lens_nucleus_anterior",
7908
7915
  z: 4.146,
7909
7916
  radius: 7.911,
7910
- n_before: 1.386,
7911
- n_after: 1.406,
7917
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7918
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7912
7919
  },
7913
7920
  {
7914
7921
  type: "spherical",
7915
7922
  name: "lens_nucleus_posterior",
7916
7923
  z: 6.565,
7917
7924
  radius: -5.76,
7918
- n_before: 1.406,
7919
- n_after: 1.386,
7925
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7926
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7920
7927
  },
7921
7928
  {
7922
7929
  type: "spherical",
7923
7930
  name: "lens_posterior",
7924
7931
  z: 7.2,
7925
7932
  radius: -6,
7926
- n_before: 1.386,
7927
- n_after: 1.336,
7933
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7934
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7928
7935
  },
7929
7936
  {
7930
7937
  type: "spherical-image",
@@ -7950,8 +7957,8 @@ NavarroParameter.parameter = {
7950
7957
  z: 0.0,
7951
7958
  radius: 7.72,
7952
7959
  conic: -0.26,
7953
- n_before: 1.0,
7954
- n_after: 1.376,
7960
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7961
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7955
7962
  },
7956
7963
  {
7957
7964
  type: "aspherical",
@@ -7959,8 +7966,8 @@ NavarroParameter.parameter = {
7959
7966
  z: 0.55,
7960
7967
  radius: 6.5,
7961
7968
  conic: 0.0,
7962
- n_before: 1.376,
7963
- n_after: 1.336,
7969
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7970
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7964
7971
  },
7965
7972
  {
7966
7973
  type: "aspherical",
@@ -7968,8 +7975,8 @@ NavarroParameter.parameter = {
7968
7975
  z: 0.55 + 3.05,
7969
7976
  radius: 10.2,
7970
7977
  conic: -3.13,
7971
- n_before: 1.336,
7972
- n_after: 1.42,
7978
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7979
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7973
7980
  },
7974
7981
  {
7975
7982
  type: "aspherical",
@@ -7977,8 +7984,8 @@ NavarroParameter.parameter = {
7977
7984
  z: 0.55 + 3.05 + 4.0,
7978
7985
  radius: -6,
7979
7986
  conic: -1,
7980
- n_before: 1.42,
7981
- n_after: 1.336,
7987
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7988
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7982
7989
  },
7983
7990
  {
7984
7991
  type: "spherical-image",
@@ -8337,6 +8344,13 @@ class ApertureStopSurface extends Surface {
8337
8344
  }
8338
8345
  }
8339
8346
 
8347
+ const DegToTABO = (degree) => {
8348
+ const d = Number(degree);
8349
+ if (!Number.isFinite(d))
8350
+ return 0;
8351
+ return (((180 - d) % 180) + 180) % 180;
8352
+ };
8353
+
8340
8354
  class ToricSurface extends Surface {
8341
8355
  constructor(props) {
8342
8356
  super({ type: "toric", name: props.name, position: props.position, tilt: props.tilt });
@@ -8552,7 +8566,8 @@ class STSurface extends Surface {
8552
8566
  const { s, c, ax, n_before = 1.0, n = 1.0, n_after = n_before, referencePoint, thickness = ST_DEFAULT_THICKNESS_MM, } = props;
8553
8567
  this.s = s;
8554
8568
  this.c = c;
8555
- this.ax = ax;
8569
+ // paraxial-surface와 동일: 처방 축은 TABO(°), 굴절/기하에는 수학 좌표 각(0~180)로 변환해 저장한다.
8570
+ this.ax = DegToTABO(ax);
8556
8571
  this.n_before = normalizeRefractiveIndexSpec(n_before);
8557
8572
  this.n = normalizeRefractiveIndexSpec(n);
8558
8573
  this.n_after = normalizeRefractiveIndexSpec(n_after);
@@ -8801,13 +8816,6 @@ const TABOToDeg = (TABOAngle) => {
8801
8816
  return (((180 - t) % 180) + 180) % 180;
8802
8817
  };
8803
8818
 
8804
- const DegToTABO = (degree) => {
8805
- const d = Number(degree);
8806
- if (!Number.isFinite(d))
8807
- return 0;
8808
- return (((180 - d) % 180) + 180) % 180;
8809
- };
8810
-
8811
8819
  /**
8812
8820
  * legacy simulator.js를 TypeScript로 옮긴 핵심 시뮬레이터입니다.
8813
8821
  * - 광원 광선을 생성하고
@@ -9175,12 +9183,19 @@ class SCAXEngineCore {
9175
9183
  }
9176
9184
  return { m, j0, j45 };
9177
9185
  }
9186
+ /** 난시 주경선 TABO 각도: 180° 동치이므로 표시·비교는 항상 [0, 180)으로 맞춘다. */
9187
+ normalizeTaboMeridian180(value) {
9188
+ const d = Number(value ?? 0);
9189
+ if (!Number.isFinite(d))
9190
+ return 0;
9191
+ return ((d % 180) + 180) % 180;
9192
+ }
9178
9193
  principalMeridiansFromVector(m, j0, j45) {
9179
9194
  if (!Number.isFinite(m) || !Number.isFinite(j0) || !Number.isFinite(j45))
9180
9195
  return [];
9181
9196
  const axisDeg = (((0.5 * Math.atan2(j45, j0) * 180) / Math.PI) % 180 + 180) % 180;
9182
- const taboAxis = this.normalizeAngle360(TABOToDeg(axisDeg));
9183
- const orthogonalTabo = this.normalizeAngle360(taboAxis + 90);
9197
+ const taboAxis = this.normalizeTaboMeridian180(TABOToDeg(axisDeg));
9198
+ const orthogonalTabo = (taboAxis + 90) % 180;
9184
9199
  const r = Math.hypot(j0, j45);
9185
9200
  const meridians = [
9186
9201
  { tabo: taboAxis, d: m - r },
@@ -6936,6 +6936,13 @@
6936
6936
  d: 1.406,
6937
6937
  C: 1.405318,
6938
6938
  },
6939
+ // Navarro 수정체 중심 굴절률(d=1.42)용 분산 스펙
6940
+ lens_navarro: {
6941
+ F: 1.421585,
6942
+ e: 1.420542,
6943
+ d: 1.420,
6944
+ C: 1.419318,
6945
+ },
6939
6946
  lens_anterior: {
6940
6947
  F: 1.387507,
6941
6948
  e: 1.386516,
@@ -7889,48 +7896,48 @@
7889
7896
  name: "cornea_anterior",
7890
7897
  z: 0.0,
7891
7898
  radius: 7.7,
7892
- n_before: 1.0,
7893
- n_after: 1.376,
7899
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7900
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7894
7901
  },
7895
7902
  {
7896
7903
  type: "spherical",
7897
7904
  name: "cornea_posterior",
7898
7905
  z: 0.5,
7899
7906
  radius: 6.8,
7900
- n_before: 1.376,
7901
- n_after: 1.336,
7907
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7908
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7902
7909
  },
7903
7910
  {
7904
7911
  type: "spherical",
7905
7912
  name: "lens_anterior",
7906
7913
  z: 3.6,
7907
7914
  radius: 10.0,
7908
- n_before: 1.336,
7909
- n_after: 1.386,
7915
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7916
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7910
7917
  },
7911
7918
  {
7912
7919
  type: "spherical",
7913
7920
  name: "lens_nucleus_anterior",
7914
7921
  z: 4.146,
7915
7922
  radius: 7.911,
7916
- n_before: 1.386,
7917
- n_after: 1.406,
7923
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_anterior,
7924
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7918
7925
  },
7919
7926
  {
7920
7927
  type: "spherical",
7921
7928
  name: "lens_nucleus_posterior",
7922
7929
  z: 6.565,
7923
7930
  radius: -5.76,
7924
- n_before: 1.406,
7925
- n_after: 1.386,
7931
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_anterior,
7932
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7926
7933
  },
7927
7934
  {
7928
7935
  type: "spherical",
7929
7936
  name: "lens_posterior",
7930
7937
  z: 7.2,
7931
7938
  radius: -6,
7932
- n_before: 1.386,
7933
- n_after: 1.336,
7939
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_nucleus_posterior,
7940
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7934
7941
  },
7935
7942
  {
7936
7943
  type: "spherical-image",
@@ -7956,8 +7963,8 @@
7956
7963
  z: 0.0,
7957
7964
  radius: 7.72,
7958
7965
  conic: -0.26,
7959
- n_before: 1.0,
7960
- n_after: 1.376,
7966
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.air,
7967
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7961
7968
  },
7962
7969
  {
7963
7970
  type: "aspherical",
@@ -7965,8 +7972,8 @@
7965
7972
  z: 0.55,
7966
7973
  radius: 6.5,
7967
7974
  conic: 0.0,
7968
- n_before: 1.376,
7969
- n_after: 1.336,
7975
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.cornea,
7976
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7970
7977
  },
7971
7978
  {
7972
7979
  type: "aspherical",
@@ -7974,8 +7981,8 @@
7974
7981
  z: 0.55 + 3.05,
7975
7982
  radius: 10.2,
7976
7983
  conic: -3.13,
7977
- n_before: 1.336,
7978
- n_after: 1.42,
7984
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.aqueous,
7985
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7979
7986
  },
7980
7987
  {
7981
7988
  type: "aspherical",
@@ -7983,8 +7990,8 @@
7983
7990
  z: 0.55 + 3.05 + 4.0,
7984
7991
  radius: -6,
7985
7992
  conic: -1,
7986
- n_before: 1.42,
7987
- n_after: 1.336,
7993
+ n_before: FRAUNHOFER_REFRACTIVE_INDICES.lens_navarro,
7994
+ n_after: FRAUNHOFER_REFRACTIVE_INDICES.vitreous,
7988
7995
  },
7989
7996
  {
7990
7997
  type: "spherical-image",
@@ -8343,6 +8350,13 @@
8343
8350
  }
8344
8351
  }
8345
8352
 
8353
+ const DegToTABO = (degree) => {
8354
+ const d = Number(degree);
8355
+ if (!Number.isFinite(d))
8356
+ return 0;
8357
+ return (((180 - d) % 180) + 180) % 180;
8358
+ };
8359
+
8346
8360
  class ToricSurface extends Surface {
8347
8361
  constructor(props) {
8348
8362
  super({ type: "toric", name: props.name, position: props.position, tilt: props.tilt });
@@ -8558,7 +8572,8 @@
8558
8572
  const { s, c, ax, n_before = 1.0, n = 1.0, n_after = n_before, referencePoint, thickness = ST_DEFAULT_THICKNESS_MM, } = props;
8559
8573
  this.s = s;
8560
8574
  this.c = c;
8561
- this.ax = ax;
8575
+ // paraxial-surface와 동일: 처방 축은 TABO(°), 굴절/기하에는 수학 좌표 각(0~180)로 변환해 저장한다.
8576
+ this.ax = DegToTABO(ax);
8562
8577
  this.n_before = normalizeRefractiveIndexSpec(n_before);
8563
8578
  this.n = normalizeRefractiveIndexSpec(n);
8564
8579
  this.n_after = normalizeRefractiveIndexSpec(n_after);
@@ -8807,13 +8822,6 @@
8807
8822
  return (((180 - t) % 180) + 180) % 180;
8808
8823
  };
8809
8824
 
8810
- const DegToTABO = (degree) => {
8811
- const d = Number(degree);
8812
- if (!Number.isFinite(d))
8813
- return 0;
8814
- return (((180 - d) % 180) + 180) % 180;
8815
- };
8816
-
8817
8825
  /**
8818
8826
  * legacy simulator.js를 TypeScript로 옮긴 핵심 시뮬레이터입니다.
8819
8827
  * - 광원 광선을 생성하고
@@ -9181,12 +9189,19 @@
9181
9189
  }
9182
9190
  return { m, j0, j45 };
9183
9191
  }
9192
+ /** 난시 주경선 TABO 각도: 180° 동치이므로 표시·비교는 항상 [0, 180)으로 맞춘다. */
9193
+ normalizeTaboMeridian180(value) {
9194
+ const d = Number(value ?? 0);
9195
+ if (!Number.isFinite(d))
9196
+ return 0;
9197
+ return ((d % 180) + 180) % 180;
9198
+ }
9184
9199
  principalMeridiansFromVector(m, j0, j45) {
9185
9200
  if (!Number.isFinite(m) || !Number.isFinite(j0) || !Number.isFinite(j45))
9186
9201
  return [];
9187
9202
  const axisDeg = (((0.5 * Math.atan2(j45, j0) * 180) / Math.PI) % 180 + 180) % 180;
9188
- const taboAxis = this.normalizeAngle360(TABOToDeg(axisDeg));
9189
- const orthogonalTabo = this.normalizeAngle360(taboAxis + 90);
9203
+ const taboAxis = this.normalizeTaboMeridian180(TABOToDeg(axisDeg));
9204
+ const orthogonalTabo = (taboAxis + 90) % 180;
9190
9205
  const r = Math.hypot(j0, j45);
9191
9206
  const meridians = [
9192
9207
  { tabo: taboAxis, d: m - r },
@@ -93,6 +93,12 @@ export declare const FRAUNHOFER_REFRACTIVE_INDICES: {
93
93
  d: number;
94
94
  C: number;
95
95
  };
96
+ lens_navarro: {
97
+ F: number;
98
+ e: number;
99
+ d: number;
100
+ C: number;
101
+ };
96
102
  lens_anterior: {
97
103
  F: number;
98
104
  e: number;
@@ -1,3 +1,4 @@
1
+ import { RefractiveIndexSpec } from "../../optics/refractive-index";
1
2
  import Surface from "../../surfaces/surface";
2
3
  interface BaseEyeSurfaceParameter {
3
4
  name: string;
@@ -6,15 +7,15 @@ interface BaseEyeSurfaceParameter {
6
7
  interface SphericalEyeSurfaceParameter extends BaseEyeSurfaceParameter {
7
8
  type: "spherical";
8
9
  radius: number;
9
- n_before: number;
10
- n_after: number;
10
+ n_before: RefractiveIndexSpec;
11
+ n_after: RefractiveIndexSpec;
11
12
  }
12
13
  interface AsphericalEyeSurfaceParameter extends BaseEyeSurfaceParameter {
13
14
  type: "aspherical";
14
15
  radius: number;
15
16
  conic: number;
16
- n_before: number;
17
- n_after: number;
17
+ n_before: RefractiveIndexSpec;
18
+ n_after: RefractiveIndexSpec;
18
19
  }
19
20
  interface SphericalImageEyeSurfaceParameter extends BaseEyeSurfaceParameter {
20
21
  type: "spherical-image";
@@ -294,6 +294,8 @@ export declare class SCAXEngineCore {
294
294
  private isRayInsidePupil;
295
295
  private powerVectorFromCylinder;
296
296
  private aggregatePowerVector;
297
+ /** 난시 주경선 TABO 각도: 180° 동치이므로 표시·비교는 항상 [0, 180)으로 맞춘다. */
298
+ private normalizeTaboMeridian180;
297
299
  private principalMeridiansFromVector;
298
300
  private principalMeridiansFromPowers;
299
301
  private normalizePrismAmount;
@@ -17,6 +17,7 @@ export type STSurfaceProps = {
17
17
  r?: number;
18
18
  s: number;
19
19
  c: number;
20
+ /** 실린더 축, TABO(°). 내부 저장은 TABOToDeg 변환 후 각도이다. */
20
21
  ax: number;
21
22
  n_before: RefractiveIndexSpec;
22
23
  n: RefractiveIndexSpec;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scax-engine",
3
- "version": "0.1.0",
3
+ "version": "0.1.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",