Skip to content

Commit 6323cc2

Browse files
committed
feat: Add export settings functionality and related tests
- Introduced ExportSettings.h for managing export configurations including size presets, quality settings, and background modes. - Implemented validation functions for export dimensions and aspect ratio locking. - Added new enums for export formats and quality modes. - Enhanced Application.h to include export settings and related properties. - Updated PlotPanel to conditionally render a canvas border based on user settings. - Bumped version to 1.5.1 to reflect new features. - Created ExportSettingsTests.cpp to cover unit tests for export settings logic.
1 parent 5c5c0de commit 6323cc2

12 files changed

Lines changed: 934 additions & 162 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The app supports:
1818
- `f(x,y,z)` scalar-field cross-sections at configurable `z` slices
1919
- `F(x,y,z)=0` implicit 3D surfaces (e.g. spheres and torus equations)
2020
- Auto/Force 2D/3D rendering preference with projected 3D grid-plane interleaving (`z=0`) for clearer depth ordering
21-
- Plot export dialog (size/aspect, color or grayscale, background color, include/exclude grid/coordinates/wires) with `.png`/`.bmp` save and clipboard copy
21+
- Plot export dialog with size presets/scales, background modes including transparency, scene toggles, export-only quality/supersampling, `.png`/`.bmp` save, clipboard copy, and post-save actions
2222
- Startup/manual update checks against GitHub releases with quick link to the releases page
2323
- Versioned binary metadata and installer packaging (`.msi` + setup `.exe`)
2424

doc/run-guide.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,22 +153,25 @@ After launch:
153153
Use the sidebar **Export** section:
154154

155155
1. Click **Open Export Dialog...**.
156-
2. Configure export options:
157-
- output `Width` / `Height`
158-
- **Lock Aspect Ratio** (or free width/height)
159-
- **Color** vs **Grayscale**
160-
- exported **Background Color** and **Background Opacity**
161-
- include/exclude **Grid**, **Coordinates**, and **Wires / Wireframe**
162-
- include/exclude **Envelope Box (3D)**
163-
- use the **Preview** section to inspect export settings and click **Refresh Preview** after major changes if needed
164-
3. Click **Copy To Clipboard** to copy the exported plot image.
165-
4. Click **Save To File...** to save the exported plot as `.png` or `.bmp`.
156+
2. Configure export options in the tabbed settings pane:
157+
- **Size**: choose the current viewport, a common preset, scale `1x`-`4x`, or custom `Width` / `Height`.
158+
- **Appearance**: choose current, transparent, white, black, or custom background, and color vs grayscale output.
159+
- **Scene**: include/exclude grid, coordinates, wires, envelope, and the axis triad.
160+
- **Quality**: optionally override interactive quality for the export render only, including surface density, implicit-surface resolution, wire thickness scale, and supersampling.
161+
- **Output**: choose `.png` or `.bmp` (PNG is the default) and optional post-save actions such as opening the image, showing it in Explorer, or copying the saved path.
162+
3. Use the right-side preview pane:
163+
- click **Refresh Preview** for a manual preview render.
164+
- enable **Auto Refresh** to refresh after a short debounce while changing settings.
165+
- transparent exports are shown over a checkerboard background.
166+
4. Click **Copy To Clipboard** to copy the exported plot image.
167+
5. Click **Save To File...** to save the exported plot as `.png` or `.bmp`.
166168

167169
Notes:
168170

169171
- Export uses the current formulas and current view/zoom.
170172
- Background/grid/coordinate/wire/envelope export options are applied only to an offscreen export render pass (the on-screen plot is not used as the export source).
171-
- Export size is used as the offscreen render size (fallback screen-capture path may resample if offscreen export fails).
173+
- Export size is the final image size. Supersampling, when enabled, renders a larger offscreen buffer and downsamples to the selected output size.
174+
- Quality overrides are applied only to preview/export rendering and do not mutate the interactive plot quality settings.
172175
- Transparent backgrounds are supported in PNG export. Some viewers may display fully transparent pixels as black because the RGB value of fully transparent pixels is not visually meaningful.
173176

174177
## Version Details / Build Metadata
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// ExportSettingsTests.cpp - Unit tests for export dialog helper logic.
2+
#include "CppUnitTest.h"
3+
#include "../XpressFormula/UI/ExportSettings.h"
4+
5+
#include <cmath>
6+
7+
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
8+
using namespace XpressFormula::UI;
9+
10+
namespace XpressFormulaTests {
11+
12+
static void assertClose(double expected, double actual, double tolerance = 1e-6) {
13+
Assert::IsTrue(std::abs(expected - actual) <= tolerance,
14+
(std::to_wstring(expected) + L" != " + std::to_wstring(actual)).c_str());
15+
}
16+
17+
TEST_CASE(ExportSize_ClampsDimensions) {
18+
int width = 2;
19+
int height = 20000;
20+
21+
validateExportSize(width, height);
22+
23+
Assert::AreEqual(kMinExportDimension, width);
24+
Assert::AreEqual(kMaxExportDimension, height);
25+
}
26+
27+
TEST_CASE(ExportSize_AspectLockedHeightPreservesRatio) {
28+
const int height = aspectLockedHeight(1920, 1280, 720);
29+
30+
Assert::AreEqual(1080, height);
31+
}
32+
33+
TEST_CASE(ExportSize_AspectLockedWidthPreservesRatio) {
34+
const int width = aspectLockedWidth(1440, 1280, 720);
35+
36+
Assert::AreEqual(2560, width);
37+
}
38+
39+
TEST_CASE(ExportSize_PresetsIncludeCurrentAndCustom) {
40+
const auto& presets = exportSizePresets();
41+
42+
Assert::IsTrue(presets.front().useCurrentSize);
43+
Assert::IsFalse(presets.front().custom);
44+
Assert::IsTrue(presets.back().custom);
45+
Assert::AreEqual(3840, presets[4].width);
46+
Assert::AreEqual(2160, presets[4].height);
47+
}
48+
49+
TEST_CASE(ExportSize_MemoryEstimateIncludesSupersampling) {
50+
const auto oneX = estimateRgbaBufferBytes(1920, 1080, ExportSupersampling::Off);
51+
const auto twoX = estimateRgbaBufferBytes(1920, 1080, ExportSupersampling::X2);
52+
53+
Assert::AreEqual(1920ull * 1080ull * 4ull, oneX);
54+
Assert::AreEqual(oneX * 4ull, twoX);
55+
}
56+
57+
TEST_CASE(ExportSize_EffectiveSupersamplingPreservesMaxDimension) {
58+
const int scale = effectiveSupersamplingFactor(8192, 4096, ExportSupersampling::X4);
59+
60+
Assert::AreEqual(1, scale);
61+
}
62+
63+
TEST_CASE(ExportSize_BytesToMiB) {
64+
assertClose(1.0, bytesToMiB(1024ull * 1024ull));
65+
}
66+
67+
TEST_CASE(ExportQuality_HighPresetMapsToExpectedValues) {
68+
const auto quality = qualitySettingsForPreset(ExportQualityPreset::High);
69+
70+
Assert::AreEqual(ExportQualityPreset::High, quality.preset);
71+
Assert::AreEqual(80, quality.surfaceResolution);
72+
Assert::AreEqual(96, quality.implicitSurfaceResolution);
73+
assertClose(1.25, quality.wireThicknessScale);
74+
Assert::AreEqual(ExportSupersampling::X2, quality.supersampling);
75+
}
76+
77+
TEST_CASE(ExportQuality_UltraUsesHighestSupersampling) {
78+
const auto quality = qualitySettingsForPreset(ExportQualityPreset::Ultra);
79+
80+
Assert::AreEqual(ExportSupersampling::X4, quality.supersampling);
81+
Assert::IsTrue(quality.surfaceResolution > qualitySettingsForPreset(ExportQualityPreset::High).surfaceResolution);
82+
}
83+
84+
TEST_CASE(ExportQuality_CustomStartsFromNormalValues) {
85+
const auto custom = qualitySettingsForPreset(ExportQualityPreset::Custom);
86+
const auto normal = qualitySettingsForPreset(ExportQualityPreset::Normal);
87+
88+
Assert::AreEqual(ExportQualityPreset::Custom, custom.preset);
89+
Assert::AreEqual(normal.surfaceResolution, custom.surfaceResolution);
90+
Assert::AreEqual(normal.implicitSurfaceResolution, custom.implicitSurfaceResolution);
91+
Assert::AreEqual(normal.supersampling, custom.supersampling);
92+
}
93+
94+
} // namespace XpressFormulaTests

src/XpressFormula.Tests/XpressFormula.Tests.vcxproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@
150150
<ClCompile Include="ViewTransformTests.cpp" />
151151
<ClCompile Include="FormulaEntryTests.cpp" />
152152
<ClCompile Include="UpdateVersionUtilsTests.cpp" />
153+
<ClCompile Include="ExportSettingsTests.cpp" />
153154
</ItemGroup>
154155
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
155156
<ImportGroup Label="ExtensionTargets" />
156-
</Project>
157+
</Project>

0 commit comments

Comments
 (0)