From e555f7b0ce242836985fd629f98b2e89c54f7c29 Mon Sep 17 00:00:00 2001 From: Paullo9 Date: Thu, 5 Nov 2026 12:02:10 +0100 Subject: [PATCH] fix: the EDS results header and exports name the kept region The results header, the export file stem, the results and spectrum CSVs, the .msa title and Auto ID's region now read the kept region's own name (Region 1, Region 2, or what it was renamed to) instead of "Region" for every shape, so two regions' exports no longer overwrite each other. Reported in mac4DSTEM/macTEM#99. Done: the kept region's name reaches the header and the exports; next: macTEM takes the next MacEMUI version. Co-Authored-By: Claude Opus 5.5 --- Sources/ShellEDXModel/EDXSession.swift | 16 ++++++++++------ Tests/ShellEDXTests/EDXRegionsTests.swift | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Sources/ShellEDXModel/EDXSession.swift b/Sources/ShellEDXModel/EDXSession.swift index 8920ebe..7ff916e 100644 --- a/Sources/ShellEDXModel/EDXSession.swift +++ b/Sources/ShellEDXModel/EDXSession.swift @@ -244,13 +244,17 @@ public final class EDXSession { public var beamEnergy: Double? { method.beamEnergyKeV ?? fileBeamEnergy } /// A one-pixel file (an .msa, a single Velox spectrum): there are no maps, no region to draw, and the words say "Spectrum", not "Whole map". public var isSingleSpectrum: Bool { (source?.nx ?? 0) * (source?.ny ?? 0) == 1 } - public var regionName: String { isSingleSpectrum ? "Spectrum" : region.name } + /// The selected region's name as the person reads it: the kept region's own ("Region 2", or what it was renamed to), "Region" for a + /// shape not kept, "Whole map", or "Spectrum" for a one-pixel file. The results header, the export names and the CSVs' region lines. + public var regionName: String { isSingleSpectrum ? "Spectrum" : keptName } + /// The kept region's name, else the region's ("Region", "Whole map"). + private var keptName: String { selectedPin?.name ?? region.name } public var canQuantify: Bool { hasImage && picks.contains { $0.role == .quantify } } public var quantifyBlocker: String? { hasImage && !canQuantify ? "Pick at least one element to quantify." : nil } /// The default stem of an export file: the image, the region. - public var exportStem: String { SpectroscopyExport.fileStem(imageName: fileName, regionName: region.name) } + public var exportStem: String { SpectroscopyExport.fileStem(imageName: fileName, regionName: keptName) } // MARK: Opening @@ -735,7 +739,7 @@ public final class EDXSession { } autoID = .running let region = region, cache = cache, listed = picks.map(\.symbol), computedK = method.kFactorSource == .computed - let regionName = region.name + let regionName = keptName autoIDRun += 1 let run = autoIDRun let task = Task.detached(priority: .userInitiated) { [weak self] in @@ -1058,16 +1062,16 @@ public final class EDXSession { // MARK: Export - public var resultsCSV: String? { fit.map { EDXCompute.resultsCSV($0, regionName: region.name, host: host) } } + public var resultsCSV: String? { fit.map { EDXCompute.resultsCSV($0, regionName: keptName, host: host) } } public var methodJSON: String? { fit.map { SpectroscopyExport.methodJSON($0.method) } } /// The region's spectrum as an `.msa` file (FormatoKit's writer); nil before there is a spectrum. public func spectrumMSA() throws -> Data? { guard let source, !spectrumCounts.isEmpty else { return nil } - return try EDXExports.msa(counts: spectrumCounts, axis: source.energyAxis, title: "\(fileName) \(region.name)", beamEnergyKeV: beamEnergy, host: host) + return try EDXExports.msa(counts: spectrumCounts, axis: source.energyAxis, title: "\(fileName) \(keptName)", beamEnergyKeV: beamEnergy, host: host) } public var spectrumCSV: String? { guard let source, !spectrumCounts.isEmpty else { return nil } - return EDXCompute.spectrumCSV(counts: spectrumCounts, axis: source.energyAxis, fileName: fileName, regionName: region.name, host: host) + return EDXCompute.spectrumCSV(counts: spectrumCounts, axis: source.energyAxis, fileName: fileName, regionName: keptName, host: host) } } diff --git a/Tests/ShellEDXTests/EDXRegionsTests.swift b/Tests/ShellEDXTests/EDXRegionsTests.swift index 3b1b64f..8f99e72 100644 --- a/Tests/ShellEDXTests/EDXRegionsTests.swift +++ b/Tests/ShellEDXTests/EDXRegionsTests.swift @@ -31,6 +31,26 @@ nonisolated final class EDXRegionsTests: XCTestCase { // MARK: Several regions + /// The results header, the export stem and the CSVs name the kept region by its own name, so two regions' exports do not overwrite each other + /// (seen 2026-11-05 on the owner's 1339 file: "Results · Region" and "…_Region quantification.csv" for Region 1 and Region 2 alike). (A shape selected but not kept + /// stays "Region": testShapesCoveringTheSamePixelsSumTheSameSpectrum.) Red under: `region.name` (always "Region" for a shape) in the header and the export names. + @MainActor func testTheHeaderAndTheExportsNameTheKeptRegion() async throws { + let session = await openRoom() + session.drawRectangle(PixelRect(x0: 0, y0: 0, x1: 1, y1: 1)) + session.drawRectangle(PixelRect(x0: 1, y0: 0, x1: 2, y1: 1)) + await session.settled() + XCTAssertEqual(session.regionName, "Region 2") + XCTAssertTrue(session.exportStem.hasSuffix("Region_2") || session.exportStem.hasSuffix("Region 2"), session.exportStem) + let two = session.exportStem + session.selectPin(session.pins[0].id) + await session.settled() + XCTAssertEqual(session.regionName, "Region 1") + XCTAssertNotEqual(session.exportStem, two, "the two regions' exports share a name") + XCTAssertTrue(try XCTUnwrap(session.spectrumCSV).contains("region: Region 1")) + session.renamePin(session.pins[0].id, to: "Precipitate") + XCTAssertEqual(session.regionName, "Precipitate") + } + /// Every shape drawn is kept as the next numbered region: two drawn shapes are two regions, each with its own pooled spectrum (the kit's sum of its pixels, /// exact), a table row with its pixel count and counts, and hiding one drops its layer but keeps its row. A shape drawn twice is one region; a deleted /// region's number is not reused. Red under: a second drawing replacing the first; the layer of a hidden region kept; numbers recycled.