Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ivanstepanovftw/f8efcd2ffc2b1e07f6697c3972c705b6 to your computer and use it in GitHub Desktop.
Save ivanstepanovftw/f8efcd2ffc2b1e07f6697c3972c705b6 to your computer and use it in GitHub Desktop.
Calculate lens BC (base curve) and diameter based on K readings (keratometry readings).

Custom Toric Contact Lenses Calculation

Calculate lens base curve and diameter based on keratometry readings.

BC and dia calculation algorithm is based on Soft Special Edition – Base Curve.

#!/usr/bin/env python
"""
Calculate lens BC (base curve) and diameter based on K readings (keratometry readings).
BC and dia calculation based on http://softspecialedition.com/base_curve
Glossary:
CL - Contact Lenses
OD - right patient's eye
OS - left patient's eye
HVID - horizontal visible iris diameter
K1 - flat meridian from keratometric readings
K2 - steep meridian from keratometric readings
VD - vertex distance
D - diopters
"""
import math
# Refractive readings (used to adjust prescription from glasses to contacts)
VD = 12.00 # mm. Set to 0 if it is your contact lens prescription
OD_Sph = +2.00
OD_Cyl = -0.75
OD_Axis = 20
OS_Sph = +3.50
OS_Cyl = -1.25
OS_Axis = 176
# Keratometry readings (used to calculate Base Curve and Diameter)
OD_HVID = 11.96 # mm
OD_Anterior_3mm_K1 = 38.72 # D
OD_Anterior_3mm_K2 = 39.17 # D
OD_Anterior_3mm_K_Avg = 38.94 # D, None if not specified
# OD_Anterior_3mm_K1_Axis = 20 # degrees
# OD_Anterior_3mm_K2_Axis = 110 # degrees
OS_HVID = 12.01
OS_Anterior_3mm_K1 = 38.12
OS_Anterior_3mm_K2 = 39.45
OS_Anterior_3mm_K_Avg = 38.77
# OS_Anterior_3mm_K1_Axis = 172
# OS_Anterior_3mm_K2_Axis = 82
# Constants
n0 = 1 # Refractive index of air
n1 = 1.3375 # Keratometric index
HVID_Avg = 11.8 # Average HVID discussed in the last issue of Soft Special Edition
def clamp(minimum, x, maximum):
return max(minimum, min(x, maximum))
def round4(x):
""" 0.25 step """
return round(x*4)/4
def round8(x):
""" 0.125 step """
return round(x*8)/8
def main():
OD_Toric = OD_Cyl is not None and OD_Axis is not None
OS_Toric = OS_Cyl is not None and OS_Axis is not None
def glassToContacts(sph, cyl):
# F_c = (F^(-1) - x)^(-1), where
# F_c is the power corrected for vertex distance,
# F is the original lens power, and
# x is the change in vertex distance in meters.
F_c1 = 1/((1/sph) - VD*0.001)
F_c2_uncorrected = sph + cyl
F_c2 = 1/((1/F_c2_uncorrected) - VD*0.001)
F_cyl = F_c2 - F_c1
return F_c1, F_cyl
OD_CL_Sph, OD_CL_Cyl = glassToContacts(OD_Sph, OD_Cyl)
OS_CL_Sph, OS_CL_Cyl = glassToContacts(OS_Sph, OS_Cyl)
OD_CL_Axis, OS_CL_Axis = OD_Axis, OS_Axis
# Calculating CL Diameter
"""
Once the HVID is determined we add 2.0mm to the HVID to determine our overall lens diameter for a spherical lens
(eg. HVID = 12.0mm, overall lens diameter = 14.0mm). For a toric contact lens we add 2.5mm
(eg. HVID = 12.0mm, overall lens diameter = 14.5mm) to provide adequate limbal coverage and to aid in lens stabilization.
"""
OD_CL_Diameter = OD_HVID + (2.5 if OD_Toric else 2)
OS_CL_Diameter = OS_HVID + (2.5 if OS_Toric else 2)
# Calculating mean keratometric reading
OD_K_Mean = OD_Anterior_3mm_K_Avg or (OD_Anterior_3mm_K1 + OD_Anterior_3mm_K2) / 2
OS_K_Mean = OS_Anterior_3mm_K_Avg or (OS_Anterior_3mm_K1 + OS_Anterior_3mm_K2) / 2
# Calculating effective keratometric reading
"""
Determine the “effective K,” which is a number that embodies both the central keratometric readings and the HVID.
This is done be adding 1D to the mean K for every 0.2mm that the HVID is larger than 11.8mm
(this was the average HVID discussed in the last issue of Soft Special Edition)
and subtracting 1D from the mean K for every 0.2mm that the HVID is smaller than 11.8mm.
"""
OD_K_Effective = OD_K_Mean + (OD_HVID - HVID_Avg) * 5
OS_K_Effective = OS_K_Mean + (OS_HVID - HVID_Avg) * 5
# Convert Effective K to BC
OD_CL_BC_Calculated = (n1 - n0) * 1000 / OD_K_Effective
OS_CL_BC_Calculated = (n1 - n0) * 1000 / OS_K_Effective
# "Nomogram" impl.
def fitFlatter(lens_dia):
lens_dia = clamp(12, lens_dia, 16.5)
lens_dia = round(lens_dia * 2) / 2 # round to 0.5
if math.isclose(lens_dia, 12):
return 0
return (lens_dia - 12.5) * 2 * 0.20 + 0.10
# Adjust
OD_CL_BC = OD_CL_BC_Calculated + fitFlatter(OD_CL_Diameter)
OS_CL_BC = OS_CL_BC_Calculated + fitFlatter(OS_CL_Diameter)
print(f"---")
print(f"OD_Sph: {OD_Sph:.2f}, OD_CL_Sph: {OD_CL_Sph:.2f}, rounded: {round4(OD_CL_Sph):+.2f} {round8(OD_CL_Sph):+.3f}")
print(f"OS_Sph: {OS_Sph:.2f}, OS_CL_Sph: {OS_CL_Sph:.2f}, rounded: {round4(OS_CL_Sph):+.2f} {round8(OS_CL_Sph):+.3f}")
print(f"OD_Cyl: {OD_Cyl:.2f}, OD_CL_Cyl: {OD_CL_Cyl:.2f}, rounded: {round4(OD_CL_Cyl):+.2f} {round8(OD_CL_Cyl):+.3f}")
print(f"OS_Cyl: {OS_Cyl:.2f}, OS_CL_Cyl: {OS_CL_Cyl:.2f}, rounded: {round4(OS_CL_Cyl):+.2f} {round8(OS_CL_Cyl):+.3f}")
print(f"OD_HVID: {OD_HVID:.2f} mm, OD_CL_Diameter: {OD_CL_Diameter:.2f} mm. BC will be flatter by {fitFlatter(OD_CL_Diameter):.2f} mm.")
print(f"OS_HVID: {OS_HVID:.2f} mm, OS_CL_Diameter: {OS_CL_Diameter:.2f} mm. BC will be flatter by {fitFlatter(OS_CL_Diameter):.2f} mm.")
print(f"OD_K_Mean: {OD_K_Mean:.2f} D, OD_K_Effective: {OD_K_Effective:.2f} D.")
print(f"OS_K_Mean: {OS_K_Mean:.2f} D, OS_K_Effective: {OS_K_Effective:.2f} D.")
print(f"OD_CL_BC_Calculated: {OD_CL_BC_Calculated:.2f} mm, OD_CL_BC: {OD_CL_BC:.2f} mm.")
print(f"OS_CL_BC_Calculated: {OS_CL_BC_Calculated:.2f} mm, OS_CL_BC: {OS_CL_BC:.2f} mm.")
print(f"---")
print(f"Based on your readings, your soft contact lenses should be:")
print(f"OD (right eye):")
print(f" Base Curve: {OD_CL_BC:.1f} mm")
print(f" Diameter: {OD_CL_Diameter:.1f} mm")
print(f" Sphere: {OD_CL_Sph:+.2f} (0.25 D step: {round4(OD_CL_Sph):+.2f}) (0.125 D step: {round8(OD_CL_Sph):+.3f})")
print(f" Cylinder: {OD_CL_Cyl:.2f} (0.25 D step: {round4(OD_CL_Cyl):+.2f}) (0.125 D step: {round8(OD_CL_Cyl):+.3f})")
print(f" Axis: {OD_CL_Axis:.0f}° (10° step: {round(OD_CL_Axis/10)*10})")
print(f" Based on nominal {VD:.0f} mm vertex distance")
print(f"OS (left eye):")
print(f" Base Curve: {OS_CL_BC:.1f} mm")
print(f" Diameter: {OS_CL_Diameter:.1f} mm")
print(f" Sphere: {OS_CL_Sph:+.2f} D (0.25 D step: {round4(OS_CL_Sph):+.2f}) (0.125 D step: {round8(OS_CL_Sph):+.3f})")
print(f" Cylinder: {OS_CL_Cyl:.2f} D (0.25 D step: {round4(OS_CL_Cyl):+.2f}) (0.125 D step: {round8(OS_CL_Cyl):+.3f})")
print(f" Axis: {OS_CL_Axis:.0f}° (10° step: {round(OS_CL_Axis/10)*10})")
print(f" Based on nominal {VD:.0f} mm vertex distance")
# Order custom lens: https://www.eyemaxxoutlet.com/c-vue-55-custom-toric-contact-lenses
if __name__ == '__main__':
main()
---
OD_Sph: 2.00, OD_CL_Sph: 2.05, rounded: +2.00 +2.000
OS_Sph: 3.50, OS_CL_Sph: 3.65, rounded: +3.75 +3.625
OD_Cyl: -0.75, OD_CL_Cyl: -0.78, rounded: -0.75 -0.750
OS_Cyl: -1.25, OS_CL_Cyl: -1.34, rounded: -1.25 -1.375
OD_HVID: 11.96 mm, OD_CL_Diameter: 14.46 mm. BC will be flatter by 0.90 mm.
OS_HVID: 12.01 mm, OS_CL_Diameter: 14.51 mm. BC will be flatter by 0.90 mm.
OD_K_Mean: 38.94 D, OD_K_Effective: 39.74 D.
OS_K_Mean: 38.77 D, OS_K_Effective: 39.82 D.
OD_CL_BC_Calculated: 8.49 mm, OD_CL_BC: 9.39 mm.
OS_CL_BC_Calculated: 8.48 mm, OS_CL_BC: 9.38 mm.
---
Based on your readings, your soft contact lenses should be:
OD (right eye):
  Base Curve: 9.4 mm
  Diameter:   14.5 mm
  Sphere:   +2.05 (0.25 D step: +2.00) (0.125 D step: +2.000)
  Cylinder: -0.78 (0.25 D step: -0.75) (0.125 D step: -0.750)
  Axis:     20° (10° step: 20)
  Based on nominal 12 mm vertex distance
OS (left eye):
  Base Curve: 9.4 mm
  Diameter:   14.5 mm
  Sphere:   +3.65 D (0.25 D step: +3.75) (0.125 D step: +3.625)
  Cylinder: -1.34 D (0.25 D step: -1.25) (0.125 D step: -1.375)
  Axis:     176° (10° step: 180)
  Based on nominal 12 mm vertex distance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment