fix(zemax): correct unit handling and folded-system geometry in importer#95
Open
beneaze wants to merge 3 commits into
Open
fix(zemax): correct unit handling and folded-system geometry in importer#95beneaze wants to merge 3 commits into
beneaze wants to merge 3 commits into
Conversation
Several long-standing correctness issues in the Zemax → component-editor import. Reworks parsing and conversion; adds a synthetic .zmx fixture so the previously-skipped parser/converter tests run on every PR. Parser: - UNIT MM | CM | M | IN parsed; lengths normalised to mm at parse time (DISZ/DIAM/ENPD multiplied, CURV divided). - DIAM renamed semi_diameter_mm to reflect that the Zemax card stores a semi-diameter, not a full diameter. The converter no longer divides by two again — imported lenses now render at the right physical size. - PARM N value cards parsed; length-valued PARMs on COORDBRK surfaces are UNIT-scaled. Adds is_mirror / is_coordinate_break / is_aspheric helpers. Converter: - Tracks a 2D frame (pos_x, pos_y, angle_rad) and places each interface perpendicular to the current propagation direction. COORDBRK PARMs 1 (perpendicular decenter) and 4 (in-plane tilt) update the frame. - GLAS MIRROR emits element_type=mirror; angle flips by pi after a planar reflection. Multi-mirror systems (e.g. periscopes) now unfold correctly in the 2D editor. - EVENASPH / ODDASPHE surfaces are approximated at their base radius and annotated; aspheric coefficients are not represented. - STOP flag surviving the dummy filter is annotated as "Aperture Stop". - Image surface (last) and flat air-to-air dummies are dropped. - COMM no longer overwrites the auto-generated material/curvature name. - Dead surface-sag block removed (renderer derives sag from radius). Examples: - examples/zemax_parse_simple.py replaces a 263-line copy-pasted parser (which still had the original DIAM-as-full-diameter bug) with a thin wrapper over the canonical ZemaxParser + ZemaxToInterfaceConverter. Tests: - tests/fixtures/sample.zmx: synthetic AC254-100-B prescription un-skips the parser/converter tests (was skipped on every run before). - New coverage in test_zemax_import.py: UNIT scaling (mm/cm/in/unknown), mirror mapping + direction flip, dummy/stop filtering, image-surface skip, COMM preservation, COORDBRK decenter + tilt, two-mirror periscope end-to-end, asphere annotation, stop annotation. - test_zemax_converter.py: dropped stale interfaces_v2/kind references. Test count: 15 active → 56 active (47 new + reactivated coverage). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the parser/converter changes under Added (UNIT, COORDBRK, mirror mapping, asphere/stop annotations), Changed (COMM preservation, examples wrapper), and Fixed (DIAM semi-diameter, ghost interfaces, test coverage). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Several long-standing correctness issues in the Zemax -> component-editor import. The integration worked structurally but the semantics were off in several places. This PR reworks parsing and conversion, and adds a synthetic
.zmxfixture so the previously-skipped parser/converter tests run on every PR.Parser
UNITcard parsed.MM | CM | M | INare normalised to mm at parse time:DISZ/DIAM/ENPDare multiplied by the scale;CURVis divided. Previously inch-unit files were silently treated as mm and imported 25.4x too small.DIAMis a semi-diameter, not a full diameter. Field renameddiameter->semi_diameter_mm; the converter no longer divides by two again. A 1 in achromat (DIAM 12.7) now imports as a 25.4 mm-tall component, not 12.7 mm.PARM N valueparsed. Length-valued PARMs onCOORDBRKsurfaces are UNIT-scaled. Newis_mirror/is_coordinate_break/is_aspherichelpers onZemaxSurface.Converter
(pos_x, pos_y, angle_rad)replaces the previouscumulative_x + directionscalar. Each interface line is placed perpendicular to the current propagation direction.COORDBRKrotates / translates the frame: PARM 1 = decenter perpendicular to current axis, PARM 4 = in-plane tilt about y. Out-of-plane PARMs (2/3/5) cannot be represented in 2D and are dropped.GLAS MIRRORemitselement_type=mirror;angle_rad += piafter a planar reflection. Multi-mirror systems (for example, periscopes with two non-parallel folds) now unfold correctly.EVENASPH/ODDASPHEare approximated as spheres at their base radius and annotated with(Asphere approx.). A warning is added to component notes + log service.STOPannotation: a stop surface that survives the dummy filter gets(Aperture Stop)appended to its name.COMMno longer overwrites the auto-generated name. It is prefixed instead, so part numbers and material/curvature info both survive.sagblock removed: the renderer already derives sag fromradius_of_curvature_mm.Examples
examples/zemax_parse_simple.pyis now a 75-line thin wrapper over the canonicalZemaxParser+ZemaxToInterfaceConverter. It previously had its own 263-line inline parser with the sameDIAM-as-full-diameter bug, perpetuating the misconception.Tests
tests/fixtures/sample.zmx: synthetic AC254-100-B-shaped prescription so the parser/converter tests run on every PR (they were universally skipped before because no fixture was checked in).test_zemax_import.py:TestUnitParsing- mm/cm/in/unknown scaling, including end-to-end conversion of an inch-unit prescriptionTestMirrorHandling-GLAS MIRROR-> mirror element, direction flip, curved mirrorTestSurfaceFiltering- image plane skipped, dummy air-to-air dropped, flat refractives keptTestNamePreservation-COMMpreserved alongside auto-generated descriptive nameTestCoordinateBreak- PARM parsing with UNIT scaling, in-plane tilt rotates next interface, perpendicular decenter shifts vertexTestPeriscope- two 45 degree fold mirrors leave the optical axis parallel to entry but offset in yTestAsphereAnnotation,TestStopAnnotation,TestMirrorFlagtest_zemax_converter.py: dropped staleinterfaces_v2/kindreferences that were preventing the fixture-driven tests from running..zmxfixture.Test count: 15 active -> 56 active.
Test plan
pytest tests/services/test_zemax_converter.py tests/services/test_zemax_parser.py tests/services/test_zemax_import.py- 56 passpython examples/zemax_parse_simple.py tests/fixtures/sample.zmx- prints the expected component summary (1 in achromat, 25.4 mm full aperture, 3 interfaces with right radii / material progression)test_paths_red,test_linked_assembly_service,test_storage_service) still passuv run --extra dev pytest tests/ui/test_component_editor.py::test_component_editor_imports_zemax_fixture -quv run --extra dev ruff check tests/ui/test_component_editor.pyZemaxImporter.import_file()with ThorlabsLA1131-A.zmxfrom the local vendor catalog importsLA1131-A N-BK7 Plano-Convex Lensas 2 interfaces with 50.8 mm apertureOut of scope (deliberately)
COORDBRK(y-decenter, tilt-about-x, tilt-about-z): the editor is 2D.STANDARDsurfaces (legacy alternative toCOORDBRK). Modern Zemax files useCOORDBRK.Generated with Claude Code and Codex.