Last active
September 7, 2023 01:18
-
-
Save zhanghai/4ae88f5afae4c353c0a30c0c6595efe5 to your computer and use it in GitHub Desktop.
A Gjs application for computation in an optical system comprising multiple spherical refracting surfaces
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/gjs | |
const GLib = imports.gi.GLib; | |
const Gtk = imports.gi.Gtk; | |
const Lang = imports.lang; | |
function compute(surfaces, objectDistance, maxObjectAngle, objectHeight) { | |
const k = surfaces.length; | |
const r = surfaces.map(function (surface) { return surface.radius }); | |
const n = [surfaces[0].reflectionOrDistanceLeft].concat(surfaces.map(function (surface) { return surface.reflectionRight })); | |
const d = surfaces.slice(1).map(function (surface) { return surface.reflectionOrDistanceLeft }); | |
const ll = [objectDistance], lr = []; | |
let u = maxObjectAngle; | |
let h = objectHeight; | |
let beta = 1; | |
for (let i = 0; i < k; ++i) { | |
const temp = (n[i + 1] - n[i]) / r[i] + n[i] / ll[i]; | |
if (temp == 0) { | |
return '无穷大'; | |
} | |
lr[i] = n[i + 1] / ((n[i + 1] - n[i]) / r[i] + n[i] / ll[i]); | |
ll[i + 1] = lr[i] - d[i]; | |
beta = beta * n[i] * lr[i] / n[i+1] / ll[i]; | |
} | |
h = beta * h; | |
u = n[0] * u / beta / n[k]; | |
return '相方倾角 u = ' + u + '\n' + | |
'横向放大率 β = ' + beta + '\n' + | |
'最后一面至像的距离 l = ' + lr[k - 1] + '\n' + | |
'像高 h = ' + h; | |
} | |
const OpticalSystem = new Lang.Class({ | |
Name: 'Optical System', | |
_init: function() { | |
this.application = new Gtk.Application(); | |
this.application.connect('startup', Lang.bind(this, this._onStartup)); | |
this.application.connect('activate', Lang.bind(this, this._onActivate)); | |
}, | |
_onStartup: function () { | |
this._buildUi(); | |
}, | |
_onActivate: function() { | |
this._window.present(); | |
}, | |
_buildUi: function() { | |
this._window = new Gtk.ApplicationWindow({ | |
application: this.application, | |
title: '多个折射球面构成光学系统的近轴计算', | |
default_width: 640, | |
default_height: 360, | |
window_position: Gtk.WindowPosition.CENTER | |
}); | |
const rootGrid = new Gtk.Grid({ | |
expand: true, | |
orientation: Gtk.Orientation.VERTICAL, | |
}); | |
const dataWindow = new Gtk.ScrolledWindow(); | |
const dataGrid = new Gtk.Grid({ | |
vexpand: true, | |
border_width: 16, | |
orientation: Gtk.Orientation.VERTICAL, | |
row_spacing: 8 | |
}); | |
const surfaceListLabel = new Gtk.Label({ | |
halign: Gtk.Align.START, | |
label: '折射球面:' | |
}); | |
dataGrid.add(surfaceListLabel); | |
this._surfaceListGrid = new Gtk.Grid({ | |
orientation: Gtk.Orientation.VERTICAL, | |
row_spacing: 8 | |
}); | |
dataGrid.add(this._surfaceListGrid); | |
this._onAddSurface(); | |
const addSurfaceButton = new Gtk.Button({ | |
hexpand: true, | |
label: '+' | |
}); | |
addSurfaceButton.connect('clicked', Lang.bind(this, this._onAddSurface)); | |
dataGrid.add(addSurfaceButton); | |
const objectGrid = new Gtk.Grid({ | |
column_homogeneous: true, | |
column_spacing: 16 | |
}); | |
const objectDistanceGrid = new Gtk.Grid({ | |
column_spacing: 8 | |
}); | |
objectDistanceGrid.add(new Gtk.Label({ | |
label: '物距 -l:' | |
})); | |
this._objectDistanceEntry = this._makeNumberEntry('mm'); | |
objectDistanceGrid.add(this._objectDistanceEntry); | |
objectGrid.add(objectDistanceGrid); | |
const maxObjectAngleGrid = new Gtk.Grid({ | |
column_spacing: 8 | |
}); | |
maxObjectAngleGrid.add(new Gtk.Label({ | |
label: '最大物方倾角 u:' | |
})); | |
this._maxObjectAngleEntry = this._makeNumberEntry('°'); | |
maxObjectAngleGrid.add(this._maxObjectAngleEntry); | |
objectGrid.add(maxObjectAngleGrid); | |
const objectHeightGrid = new Gtk.Grid({ | |
column_spacing: 8 | |
}); | |
objectHeightGrid.add(new Gtk.Label({ | |
label: '物高 h:' | |
})); | |
this._objectHeightEntry = this._makeNumberEntry('mm'); | |
objectHeightGrid.add(this._objectHeightEntry); | |
objectGrid.add(objectHeightGrid); | |
dataGrid.add(objectGrid); | |
dataWindow.add(dataGrid); | |
rootGrid.add(dataWindow); | |
const computeGrid = new Gtk.Grid({ | |
border_width: 16, | |
orientation: Gtk.Orientation.VERTICAL, | |
row_spacing: 8 | |
}); | |
const computeButton = new Gtk.Button({ | |
hexpand: true, | |
label: '计算' | |
}) | |
computeButton.get_style_context().add_class('suggested-action'); | |
computeButton.connect('clicked', Lang.bind(this, this._onCompute)); | |
computeButton.set_can_default(true); | |
computeGrid.add(computeButton); | |
rootGrid.add(computeGrid); | |
this._window.add(rootGrid); | |
this._window.show_all(); | |
computeButton.grab_default(); | |
}, | |
_onAddSurface: function() { | |
const surfaceGrid = new Gtk.Grid({ | |
column_spacing: 8 | |
}) | |
surfaceGrid.add(this._makeNumberEntry(this._surfaceListGrid.get_children().length === 0 ? '左侧折射率 n1' : '间距 d')); | |
surfaceGrid.add(this._makeNumberEntry('球面半径 r')); | |
surfaceGrid.add(this._makeNumberEntry('右侧折射率 n2')); | |
const removeButton = new Gtk.Button({ | |
label: '移除', | |
sensitive: this._surfaceListGrid.get_children().length !== 0 | |
}) | |
removeButton.connect('clicked', Lang.bind(this, function() { | |
this._surfaceListGrid.remove(surfaceGrid); | |
})); | |
surfaceGrid.add(removeButton); | |
this._surfaceListGrid.add(surfaceGrid); | |
surfaceGrid.show_all(); | |
}, | |
_makeNumberEntry: function(placeholder) { | |
const entry = new Gtk.Entry({ | |
hexpand: true, | |
input_purpose: Gtk.InputPurpose.NUMBER, | |
placeholder_text: placeholder, | |
width_chars: 4 | |
}); | |
entry.connect('activate', Lang.bind(this, this._onCompute)); | |
return entry; | |
}, | |
_onCompute: function() { | |
const surfaces = []; | |
const surfaceListGridChildren = this._surfaceListGrid.get_children(); | |
for (let i = surfaceListGridChildren.length - 1; i >= 0; --i) { | |
const surfaceGridChildren = surfaceListGridChildren[i].get_children(); | |
const index = surfaceListGridChildren.length - i; | |
const reflectionOrDistanceLeft = this._getEntryFloat(surfaceGridChildren[3], '第 ' + index + ' 个球面' + (index === 1 ? '左侧折射率' : '间距') + '无效'); | |
if (!reflectionOrDistanceLeft) { | |
return; | |
} | |
const radius = this._getEntryFloat(surfaceGridChildren[2], '第 ' + index + ' 个球面半径无效'); | |
if (!radius) { | |
return; | |
} | |
const reflectionRight = this._getEntryFloat(surfaceGridChildren[1], '第 ' + index + ' 个球面右侧折射率无效'); | |
if (!reflectionRight) { | |
return; | |
} | |
surfaces.push({ | |
reflectionOrDistanceLeft: reflectionOrDistanceLeft, | |
radius: radius, | |
reflectionRight: reflectionRight | |
}); | |
} | |
const objectDistance = this._getEntryFloat(this._objectDistanceEntry, '物距无效'); | |
if (!objectDistance) { | |
return; | |
} | |
const maxObjectAngle = this._getEntryFloat(this._maxObjectAngleEntry, '最大物方倾角无效'); | |
if (!maxObjectAngle) { | |
return; | |
} | |
const objectHeight = this._getEntryFloat(this._objectHeightEntry, '物高无效'); | |
if (!objectHeight) { | |
return; | |
} | |
this._showResult(null, compute(surfaces, objectDistance, maxObjectAngle, objectHeight)); | |
}, | |
_getEntryFloat: function(entry, error) { | |
const value = parseFloat(entry.get_text()); | |
if (!value) { | |
this._showResult(error, null); | |
entry.grab_focus(); | |
} | |
return value; | |
}, | |
_showResult: function(error, result) { | |
const dialog = new Gtk.MessageDialog({ | |
transient_for: this._window, | |
modal: true, | |
message_type: error ? Gtk.MessageType.ERROR : Gtk.MessageType.INFO, | |
text: error ? '错误' : '计算结果', | |
secondary_text: error || result, | |
buttons: Gtk.ButtonsType.OK | |
}) | |
dialog.run() | |
dialog.destroy() | |
} | |
}); | |
const app = new OpticalSystem(); | |
app.application.run(ARGV); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment