Center button to center color scale range#25
Center button to center color scale range#25cmorency1 wants to merge 1 commit intoSEATStandards:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a “Center” control to the data range UI in NcVis to quickly set the color scale range symmetric about 0, improving visualization of signed fields.
Changes:
- Added a new “Center” button next to the range controls and wired it into the event table.
- Implemented
SetDataRangeCenteredOnZero()to compute min/max (respecting missing values) and set a symmetric[-M, M]range. - Added the
OnRangeCenterMinMaxcallback and corresponding declarations.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| src/wxNcVisFrame.h | Declares the new centering range method and the new button callback. |
| src/wxNcVisFrame.cpp | Implements centered-on-zero range logic, adds button + event wiring, and hooks callback into the UI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void wxNcVisFrame::SetDataRangeCenteredOnZero(bool fRedraw) { | ||
| if (m_data.size() == 0) { | ||
| return; | ||
| } | ||
|
|
||
| float dDataMin = 0.0f; | ||
| float dDataMax = 0.0f; | ||
|
|
||
| int i; | ||
|
|
||
| if ((!m_fDataHasMissingValue) || (std::isnan(m_dMissingValueFloat))) { | ||
| for (i = 0; i < (int)m_data.size(); i++) { | ||
| if (!std::isnan(m_data[i])) { | ||
| break; | ||
| } | ||
| } | ||
| if (i == (int)m_data.size()) { | ||
| return; // no valid data | ||
| } | ||
|
|
||
| dDataMin = m_data[i]; | ||
| dDataMax = m_data[i]; | ||
|
|
||
| for (i++; i < (int)m_data.size(); i++) { | ||
| if (std::isnan(m_data[i])) { | ||
| continue; | ||
| } | ||
| if (m_data[i] < dDataMin) dDataMin = m_data[i]; | ||
| if (m_data[i] > dDataMax) dDataMax = m_data[i]; | ||
| } | ||
|
|
||
| } else { | ||
| for (i = 0; i < (int)m_data.size(); i++) { | ||
| if ((m_data[i] != m_dMissingValueFloat) && (!std::isnan(m_data[i]))) { | ||
| break; | ||
| } | ||
| } | ||
| if (i == (int)m_data.size()) { | ||
| return; // no valid data | ||
| } | ||
|
|
||
| dDataMin = m_data[i]; | ||
| dDataMax = m_data[i]; | ||
|
|
||
| for (i++; i < (int)m_data.size(); i++) { | ||
| if ((m_data[i] == m_dMissingValueFloat) || (std::isnan(m_data[i]))) { | ||
| continue; | ||
| } | ||
| if (m_data[i] < dDataMin) dDataMin = m_data[i]; | ||
| if (m_data[i] > dDataMax) dDataMax = m_data[i]; | ||
| } | ||
| } |
There was a problem hiding this comment.
SetDataRangeCenteredOnZero() largely duplicates the min/max scan logic from SetDataRangeByMinMax(), but with slightly different edge-case handling. To reduce maintenance burden (and avoid the two implementations drifting), consider extracting a shared helper that computes (min,max) over valid data and reusing it from both functions.
| //////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| void wxNcVisFrame::OnRangeCenterMinMax( | ||
| wxCommandEvent & event | ||
| ) { | ||
| SetDataRangeCenteredOnZero(true); | ||
| } |
There was a problem hiding this comment.
The new OnRangeCenterMinMax block has inconsistent indentation and duplicate separator lines (two consecutive "////////////////////////////////////////////////////////////////////////////////" plus trailing whitespace). Please match the existing formatting style in this file (tabs/indentation and a single separator) to keep diffs clean and consistent.
| //////////////////////////////////////////////////////////////////////////////// | |
| void wxNcVisFrame::OnRangeCenterMinMax( | |
| wxCommandEvent & event | |
| ) { | |
| SetDataRangeCenteredOnZero(true); | |
| } | |
| void wxNcVisFrame::OnRangeCenterMinMax( | |
| wxCommandEvent & event | |
| ) { | |
| SetDataRangeCenteredOnZero(true); | |
| } |
| /// <summary> | ||
| /// Set the data range centered on zero. | ||
| /// </summary> |
There was a problem hiding this comment.
The newly added XML doc comments use different indentation/alignment than the surrounding header (which consistently uses tab-indented "///\t
"). Please reformat these comments to match the established style in this file for consistency.
Suggested change
/// <summary>
/// Set the data range centered on zero.
/// </summary>
/// <summary>
/// Set the data range centered on zero.
/// </summary>
| /// <summary> | |
| /// Set the data range centered on zero. | |
| /// </summary> | |
| /// <summary> | |
| /// Set the data range centered on zero. | |
| /// </summary> |
| /// <summary> | ||
| /// Callback triggered when the range center min/max button is pressed. | ||
| /// </summary> |
There was a problem hiding this comment.
The newly added XML doc comments for OnRangeCenterMinMax use different indentation/alignment than the surrounding header comment blocks. Please align them with the existing tab-indented comment style used throughout this header for consistency.
| /// <summary> | |
| /// Callback triggered when the range center min/max button is pressed. | |
| /// </summary> | |
| /// <summary> | |
| /// Callback triggered when the range center min/max button is pressed. | |
| /// </summary> |
| m_vecwxRange[0]->Enable(true); | ||
| m_vecwxRange[1]->Enable(true); | ||
|
|
||
| SetDataRangeCenteredOnZero(false); |
There was a problem hiding this comment.
GenerateDimensionControls() calls SetDataRangeCenteredOnZero(false) and then immediately calls SetDataRangeByMinMax(false), which overwrites the centered range and scans the dataset twice. Since centering is triggered by the new "Center" button, the first call appears redundant; consider removing it (or, if centering is meant to be the default, remove/adjust the subsequent SetDataRangeByMinMax(false) so the centered range is preserved).
| SetDataRangeCenteredOnZero(false); |
| // Center on 0: make range symmetric around zero | ||
| const float M = std::max(std::fabs(dDataMin), std::fabs(dDataMax)); | ||
| if (!(M > 0.0f) || std::isnan(M)) { |
There was a problem hiding this comment.
SetDataRangeCenteredOnZero() uses std::max but this file doesn't include directly. Relying on transitive includes can break portability; please add the appropriate standard header include for std::max (and keep math includes consistent with std::fabs/std::isnan usage).
| if (!(M > 0.0f) || std::isnan(M)) { | ||
| return; | ||
| } | ||
|
|
||
| m_imagepanel->SetDataRange(-M, M, fRedraw); |
There was a problem hiding this comment.
SetDataRangeCenteredOnZero() returns early when M == 0, which means for an all-zero field the "Center" button will do nothing and the range may remain non-centered (e.g., the default 0..1). Consider explicitly handling the degenerate case (min==max==0) by setting a small symmetric range around zero (or a fixed fallback range) instead of returning.
| if (!(M > 0.0f) || std::isnan(M)) { | |
| return; | |
| } | |
| m_imagepanel->SetDataRange(-M, M, fRedraw); | |
| // If M is NaN, there is no valid magnitude to use. | |
| if (std::isnan(M)) { | |
| return; | |
| } | |
| // For valid data with zero magnitude (e.g., all-zero field), use a small | |
| // symmetric fallback range around zero instead of doing nothing. | |
| float rangeMax = M; | |
| if (!(M > 0.0f)) { | |
| rangeMax = 1.0f; // fallback half-range for degenerate all-zero fields | |
| } | |
| m_imagepanel->SetDataRange(-rangeMax, rangeMax, fRedraw); |
This is a new button next to range. Open ncvis, click Center and verify that the range is now centered on 0.