Skip to content

PR-L3 — Layer Controls UI#120

Merged
philliphoff merged 3 commits into
mainfrom
philliphoff/pr-l3-layer-controls-ui
May 22, 2026
Merged

PR-L3 — Layer Controls UI#120
philliphoff merged 3 commits into
mainfrom
philliphoff/pr-l3-layer-controls-ui

Conversation

@philliphoff
Copy link
Copy Markdown
Owner

Adds a new Layer Stack side panel that surfaces the resolved S-98 layer stack grouped by display plane (top-of-paint-stack first) and lets the user toggle each dataset's in-memory Active flag.

What's in

  • Interface promotionIDatasetLoaderService.CurrentStackedLayers and LayerStackChanged are now required members. Added CurrentStackEntries (richer LayerStackEntry snapshot) plus GetActive / SetActive / ActiveChanged.
  • Active flag (in-memory)DatasetLoaderService stores Active per dataset id (default true). FlattenLayerOrder and BuildLoadedDatasetInfos honour it, so R-101-102-B and any future Active-aware rules see the toggle. Process-local; persistence deferred to PR-L4 (TODO marker on _activeFlags). Distinct from DatasetEntry.IsVisible — documented on LoadedDatasetInfo.Active.
  • LayerStackViewModel — groups CurrentStackEntries by S98DisplayPlane in the canonical top-first order, hides empty planes by default with a 'Show empty planes' toggle, preserves per-plane expansion across rebuilds, and routes per-entry IsActive to IDatasetLoaderService.SetActive.
  • LayerStackView.axaml — mirrors CatalogPanelView. PR-L4 TBDs (PdC picker / drag-reorder unification) are marked in code.
  • MainWindow / App DI — new activity-bar toggle button + view binding; LayerStackViewModel registered as a singleton.
  • Strings & tooltips — 16 new Strings.resx entries. All checkboxes / toggle buttons carry ToolTip.Tip.

Placement assumption

The brief asked for "own column in the left sidebar, sibling to Catalogs". The existing MainWindow.axaml uses a single activity-pane column whose contents are selected by MainViewModel.SelectedActivity (driven by the activity bar). Adding a parallel column would double the sidebar chrome and break the established pattern, so I implemented Layer Stack as a new activity tab (sibling to Catalogs in the activity bar). That still satisfies "sibling to Catalogs, not a sub-tab of dataset list" — happy to refactor to a literal second column if you'd prefer.

Tests

Project Before After Δ
EncDotNet.S100.Viewer.Tests 280 290 +10
EncDotNet.S100.Pipelines.Tests 340 340 0
  • LayerStackViewModelTests (6) — plane grouping, top-first order, within-plane priority desc, empty-plane hide/show, LayerStackChanged triggers rebuild, expansion preserved across rebuilds, IsActive round-trips through the loader.
  • ActiveFlagTests (4) — LoadedDatasetInfo.Active round-trip, loader GetActive default, SetActive raises ActiveChanged. R-101-102-B Active=false suppression is already covered in S98DefaultRulesTests (PR-L2).
  • Updated 10 existing IDatasetLoaderService stubs to implement the promoted members.

dotnet build EncDotNet.S100.slnx -c Release and dotnet test -c Release both green.

Out of scope (deferred to PR-L4)

  • // TODO PR-L4 (TBD-7): PdC picker UX — empty-state area of the panel.
  • // TODO PR-L4 (TBD-11): unify ordering UX with DatasetsViewModel drag-reorder — drag-reorder stays as-is on the Datasets panel.
  • // TODO PR-L4: persist Active in ViewerSettings — Active is in-memory only this PR.

Visual regression

Panel is additive (new activity tab; no MainWindow column changes). No snapshot rebaseline needed.

Manual smoke (described, not enforced)

Load S-101 + S-102 → two planes visible (BaseChartOver/Bathymetry); toggle S-102 Active off → S-101 DepthArea reappears in render + pick.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Add a new Layer Stack side panel that surfaces the resolved S-98
layer stack grouped by display plane (top-of-paint-stack first) and
lets the user toggle each dataset's in-memory Active flag.

Implementation
- Promote IDatasetLoaderService.CurrentStackedLayers and
  LayerStackChanged from default to required members. Add a richer
  CurrentStackEntries snapshot (one LayerStackEntry per stack slot)
  plus GetActive/SetActive/ActiveChanged for the new in-memory
  Active flag.
- DatasetLoaderService stores Active per dataset id (defaults to
  true), and FlattenLayerOrder / BuildLoadedDatasetInfos honour it
  so R-101-102-B and any future Active-aware rules see the toggle.
  Active is process-local; persistence deferred to PR-L4
  (TODO marker on _activeFlags).
- LayerStackViewModel groups CurrentStackEntries by S98DisplayPlane
  in the canonical top-first order (EcdisAlerts → ... →
  BaseChartUnder), hides empty planes by default with a 'Show empty
  planes' toggle, preserves expansion state per plane across
  rebuilds, and bridges the per-entry Active checkbox to
  IDatasetLoaderService.SetActive.
- LayerStackView.axaml mirrors the CatalogPanelView pattern; PR-L4
  TBDs (PdC picker, drag-reorder unification) are marked in code.
- MainWindow adds an activity-bar toggle button for the new pane
  (sibling to Catalogs in the activity bar; the panel lives in the
  shared activity-pane column rather than its own sidebar column,
  which would conflict with the existing single-pane architecture).
- App.axaml.cs registers LayerStackViewModel in DI.

Strings & tooltips (Resources/Strings.{resx,cs})
- Pane_LayerStack, Tooltip_LayerStack, plus 14 LayerStack_* keys
  (panel title, ShowEmptyPlanes, Active tooltip and Active vs
  Visibility help text, plane-name labels for all nine S-98
  display planes, child-count and priority format strings, and
  empty-plane placeholder).

LoadedDatasetInfo
- XML doc on Active now explains Active vs DatasetEntry.IsVisible.

Tests (viewer 280 → 290, +10)
- LayerStackViewModelTests: plane grouping, top-first plane order,
  within-plane priority descending, empty-plane hide/show toggle,
  LayerStackChanged triggers rebuild, expansion state preserved
  across rebuilds, IsActive round-trips through the loader.
- ActiveFlagTests: LoadedDatasetInfo.Active round-trip, loader
  GetActive default, SetActive raises ActiveChanged. R-101-102-B
  Active=false suppression is already covered in
  S98DefaultRulesTests (PR-L2).
- Updated all 10 existing IDatasetLoaderService stubs in
  tests/EncDotNet.S100.Viewer.Tests to implement the promoted
  interface members.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Performance Gate

PASSED — no regressions.

Threshold: 10.0%, MAD multiplier (k): 3.0, retry-zone mult: 2.0×

Scenario summary

Scenario Status Δ median (%) z (Δ/MAD) Base median (ms) Samples (b/c)
exchange-set-open ✅ pass +4.3 +0.38 0.41 20/20
s101-portray-cold ✅ pass +1.2 +0.50 244.40 20/20
s101-portray-warm ✅ pass -12.8 -4.19 135.08 20/20
s101-render-warm ✅ pass -5.8 -0.98 135.76 20/20
s102-coverage ✅ pass +2.4 +1.19 1.21 20/20
s102-coverage-open ✅ pass -0.3 -0.18 2.29 20/20
s102-coverage-render-large ✅ pass -0.2 -0.18 103.49 20/20
s124-vector ✅ pass +9.4 +1.10 0.35 20/20
s201-vector ✅ pass +1.5 +0.18 0.27 20/20

exchange-set-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.41 0.43
Baseline MAD (ms) 0.05
Δ median +4.3%
z (Δ/MAD) +0.38

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.asset.read 8.70 8.61 -1.0% ▫️
s100.exchangeset.parse 31.04 30.98 -0.2% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.asset.read.duration 14.06 13.95 -0.8% ▫️

s101-portray-cold

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 244.40 247.25
Baseline MAD (ms) 5.73
Δ median +1.2%
z (Δ/MAD) +0.50

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 3262.42 3225.09 -1.1% ▫️
s100.lua.rule.invoke 2756.63 2715.07 -1.5% ▫️
s100.pipeline.vector.process 3312.76 3275.62 -1.1% ▫️
s100.pipeline.vector.stage.assemble 0.21 0.23 +8.6%
s100.pipeline.vector.stage.feature_xml 33.91 33.60 -0.9% ▫️
s100.pipeline.vector.stage.lua 3263.33 3226.09 -1.1% ▫️
s100.pipeline.vector.stage.rule_select 4.36 3.91 -10.3%
s100.pipeline.vector.stage.sort 1.64 1.64 -0.2% ▫️
s100.pipeline.vector.stage.viewing_groups 2.75 2.84 +3.1% ▫️
s100.pipeline.vector.stage.xslt 0.16 0.18 +6.5%
s100.render.frame 3950.49 4013.42 +1.6% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 7.00 7.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.lua.execute.duration 834.87 826.87 -1.0% ▫️
s100.lua.features.count 700.00 700.00 +0.0% ▫️
s100.lua.instructions.emitted.count 735.00 735.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 641.44 631.11 -1.6% ▫️
s100.lua.rule.invoke.duration 4.18 3.94 -5.8% ▫️
s100.lua.source.cache.hit.count 292.00 292.00 +0.0% ▫️
s100.lua.source.cache.miss.count 23.00 23.00 +0.0% ▫️
s100.pattern.cache.hit.count 63.00 63.00 +0.0% ▫️
s100.pattern.cache.miss.count 28.00 28.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 735.00 735.00 +0.0% ▫️
s100.pipeline.duration 857.16 849.59 -0.9% ▫️
s100.pipeline.features.in 84.00 84.00 +0.0% ▫️
s100.pipeline.stage.duration 0.21 0.20 -1.3% ▫️
s100.pipeline.stage.duration 13.51 13.94 +3.2% ▫️
s100.pipeline.stage.duration 835.77 827.77 -1.0% ▫️
s100.pipeline.stage.duration 3.22 3.20 -0.6% ▫️
s100.pipeline.stage.duration 1.87 1.88 +0.6% ▫️
s100.pipeline.stage.duration 0.68 0.74 +8.9%
s100.pipeline.stage.duration 0.69 0.67 -3.3% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.portrayal.cache.hit.count 24.00 24.00 +0.0% ▫️
s100.portrayal.cache.hit.count 292.00 292.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 36.00 36.00 +0.0% ▫️
s100.portrayal.cache.miss.count 4.00 4.00 +0.0% ▫️
s100.portrayal.cache.miss.count 23.00 23.00 +0.0% ▫️
s100.portrayal.cache.miss.count 6.00 6.00 +0.0% ▫️
s100.render.frame.duration 1059.40 1063.52 +0.4% ▫️
s100.render.instructions.processed.count 735.00 735.00 +0.0% ▫️
s100.render.styles.applied.count 693.00 693.00 +0.0% ▫️
s100.symbol.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.symbol.cache.miss.count 14.00 14.00 +0.0% ▫️
s100.symbol.resolve.duration 0.03 0.03 +3.1% ▫️
s100.symbol.resolve.duration 7.36 7.53 +2.3% ▫️

s101-portray-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 135.08 117.82
Baseline MAD (ms) 4.12
Δ median -12.8%
z (Δ/MAD) -4.19

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 3855.90 3451.67 -10.5%
s100.lua.rule.invoke 3258.90 2904.50 -10.9%
s100.pipeline.vector.process 3929.02 3520.11 -10.4%
s100.pipeline.vector.stage.assemble 0.17 0.17 -0.9% ▫️
s100.pipeline.vector.stage.feature_xml 62.67 57.76 -7.8% ▫️
s100.pipeline.vector.stage.lua 3856.83 3452.69 -10.5%
s100.pipeline.vector.stage.rule_select 3.19 3.17 -0.6% ▫️
s100.pipeline.vector.stage.sort 2.20 2.13 -3.3% ▫️
s100.pipeline.vector.stage.viewing_groups 3.46 3.33 -3.8% ▫️
s100.pipeline.vector.stage.xslt 0.17 0.18 +5.8%
s100.render.frame 57.32 63.60 +11.0%

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 881.70 856.27 -2.9% ▫️
s100.lua.features.count 700.00 700.00 +0.0% ▫️
s100.lua.instructions.emitted.count 735.00 735.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 728.74 708.16 -2.8% ▫️
s100.lua.rule.invoke.duration 16.15 14.65 -9.3% ▫️
s100.lua.source.cache.hit.count 315.00 315.00 +0.0% ▫️
s100.pattern.cache.hit.count 87.00 87.00 +0.0% ▫️
s100.pattern.cache.miss.count 4.00 4.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 735.00 735.00 +0.0% ▫️
s100.pipeline.duration 899.08 873.05 -2.9% ▫️
s100.pipeline.features.in 84.00 84.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 -2.4% ▫️
s100.pipeline.stage.duration 14.63 14.05 -4.0% ▫️
s100.pipeline.stage.duration 881.91 856.47 -2.9% ▫️
s100.pipeline.stage.duration 0.87 0.88 +1.6% ▫️
s100.pipeline.stage.duration 0.51 0.49 -4.1% ▫️
s100.pipeline.stage.duration 0.18 0.16 -9.2% ▫️
s100.pipeline.stage.duration 0.03 0.03 -5.1% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.portrayal.cache.hit.count 4.00 4.00 +0.0% ▫️
s100.portrayal.cache.hit.count 315.00 315.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.render.frame.duration 138.88 141.73 +2.1% ▫️
s100.render.instructions.processed.count 735.00 735.00 +0.0% ▫️
s100.render.styles.applied.count 693.00 693.00 +0.0% ▫️
s100.symbol.cache.hit.count 19.00 19.00 +0.0% ▫️
s100.symbol.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.symbol.resolve.duration 0.04 0.05 +8.8%
s100.symbol.resolve.duration 0.30 0.30 -2.2% ▫️

s101-render-warm

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 135.76 127.84
Baseline MAD (ms) 8.10
Δ median -5.8%
z (Δ/MAD) -0.98

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.lua.execute 3998.52 3831.51 -4.2% ▫️
s100.lua.rule.invoke 3337.74 3170.93 -5.0% ▫️
s100.pipeline.vector.process 4071.58 3904.54 -4.1% ▫️
s100.pipeline.vector.stage.assemble 0.18 0.18 -0.5% ▫️
s100.pipeline.vector.stage.feature_xml 62.44 62.84 +0.6% ▫️
s100.pipeline.vector.stage.lua 3999.43 3832.46 -4.2% ▫️
s100.pipeline.vector.stage.rule_select 3.08 2.94 -4.6% ▫️
s100.pipeline.vector.stage.sort 2.57 2.35 -8.6% ▫️
s100.pipeline.vector.stage.viewing_groups 3.74 3.54 -5.4% ▫️
s100.pipeline.vector.stage.xslt 0.17 0.17 -1.0% ▫️
s100.render.frame 47.11 48.01 +1.9% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.lua.execute.duration 928.79 835.08 -10.1%
s100.lua.features.count 700.00 700.00 +0.0% ▫️
s100.lua.instructions.emitted.count 735.00 735.00 +0.0% ▫️
s100.lua.rule.invoke.count 7.00 7.00 +0.0% ▫️
s100.lua.rule.invoke.count 77.00 77.00 +0.0% ▫️
s100.lua.rule.invoke.duration 776.87 685.27 -11.8%
s100.lua.rule.invoke.duration 3.94 3.53 -10.6%
s100.lua.source.cache.hit.count 315.00 315.00 +0.0% ▫️
s100.pattern.cache.hit.count 87.00 87.00 +0.0% ▫️
s100.pattern.cache.miss.count 4.00 4.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 735.00 735.00 +0.0% ▫️
s100.pipeline.duration 947.61 851.16 -10.2%
s100.pipeline.features.in 84.00 84.00 +0.0% ▫️
s100.pipeline.stage.duration 0.02 0.02 -7.0% ▫️
s100.pipeline.stage.duration 16.40 13.79 -15.9%
s100.pipeline.stage.duration 928.97 835.24 -10.1%
s100.pipeline.stage.duration 0.71 0.69 -2.2% ▫️
s100.pipeline.stage.duration 0.52 0.49 -4.5% ▫️
s100.pipeline.stage.duration 0.14 0.14 -1.9% ▫️
s100.pipeline.stage.duration 0.02 0.02 -6.5% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 735.00 735.00 +0.0% ▫️
s100.portrayal.cache.hit.count 4.00 4.00 +0.0% ▫️
s100.portrayal.cache.hit.count 315.00 315.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.render.frame.duration 135.19 135.11 -0.1% ▫️
s100.render.instructions.processed.count 735.00 735.00 +0.0% ▫️
s100.render.styles.applied.count 693.00 693.00 +0.0% ▫️
s100.symbol.cache.hit.count 19.00 19.00 +0.0% ▫️
s100.symbol.cache.miss.count 2.00 2.00 +0.0% ▫️
s100.symbol.resolve.duration 0.04 0.04 +2.3% ▫️
s100.symbol.resolve.duration 0.31 0.25 -18.9%

s102-coverage

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 1.21 1.24
Baseline MAD (ms) 0.02
Δ median +2.4%
z (Δ/MAD) +1.19

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 46.62 46.76 +0.3% ▫️
s100.pipeline.coverage.stage.read 3.66 3.80 +3.7% ▫️
s100.pipeline.coverage.stage.resolve 40.39 40.00 -1.0% ▫️
s100.render.coverage.build 61.73 61.95 +0.4% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 4557.00 4557.00 +0.0% ▫️
s100.hdf5.read.bytes 5208.00 5208.00 +0.0% ▫️
s100.hdf5.read.duration 16.50 16.67 +1.0% ▫️
s100.hdf5.read.duration 22.82 23.03 +0.9% ▫️
s100.hdf5.read.duration 5.86 5.67 -3.3% ▫️
s100.pipeline.duration 9.26 8.73 -5.7% ▫️
s100.pipeline.stage.duration 0.65 0.68 +3.4% ▫️
s100.pipeline.stage.duration 8.19 7.60 -7.2% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s102-coverage-open

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 2.29 2.28
Baseline MAD (ms) 0.04
Δ median -0.3%
z (Δ/MAD) -0.18

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.dataset.open 66.39 69.03 +4.0% ▫️
s100.hdf5.dataset.read 9.69 10.09 +4.1% ▫️
s100.hdf5.file.open 15.77 15.67 -0.6% ▫️
s100.hdf5.open 16.43 16.57 +0.8% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.hdf5.read.bytes 36456.00 36456.00 +0.0% ▫️
s100.hdf5.read.duration 5.32 5.48 +2.9% ▫️
s100.hdf5.read.duration 1.77 1.96 +10.7%
s100.hdf5.read.duration 3.76 3.79 +0.8% ▫️

s102-coverage-render-large

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 103.49 103.34
Baseline MAD (ms) 0.90
Δ median -0.2%
z (Δ/MAD) -0.18

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.coverage.process 177.01 155.32 -12.3%
s100.pipeline.coverage.stage.read 105.13 91.52 -12.9%
s100.pipeline.coverage.stage.resolve 69.03 60.72 -12.1%
s100.render.coverage.build 3662.70 3598.34 -1.8% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.coverage.cells 7000000.00 7000000.00 +0.0% ▫️
s100.hdf5.read.bytes 8000000.00 8000000.00 +0.0% ▫️
s100.hdf5.read.duration 0.07 0.06 -6.5% ▫️
s100.hdf5.read.duration 1.74 4.50 +158.8%
s100.hdf5.read.duration 2.05 0.97 -52.6%
s100.pipeline.duration 33.87 30.74 -9.2% ▫️
s100.pipeline.stage.duration 18.86 17.97 -4.7% ▫️
s100.pipeline.stage.duration 14.38 12.16 -15.4%
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️

s124-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.35 0.38
Baseline MAD (ms) 0.03
Δ median +9.4%
z (Δ/MAD) +1.10

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 7.62 8.58 +12.6%
s100.pipeline.vector.stage.assemble 0.25 0.29 +15.8%
s100.pipeline.vector.stage.feature_xml 1.33 1.45 +8.8%
s100.pipeline.vector.stage.rule_select 0.27 0.26 -5.4% ▫️
s100.pipeline.vector.stage.sort 0.22 0.22 +1.2% ▫️
s100.pipeline.vector.stage.viewing_groups 0.52 0.63 +20.3%
s100.pipeline.vector.stage.xslt 3.55 3.92 +10.5%
s100.render.frame 0.75 0.88 +18.2%
s100.xslt.transform 1.46 1.69 +15.5%

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 14.00 14.00 +0.0% ▫️
s100.pipeline.duration 43.90 43.43 -1.1% ▫️
s100.pipeline.features.in 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.duration 0.86 0.81 -5.6% ▫️
s100.pipeline.stage.duration 2.54 2.59 +1.9% ▫️
s100.pipeline.stage.duration 0.90 0.82 -8.5% ▫️
s100.pipeline.stage.duration 0.07 0.05 -34.9%
s100.pipeline.stage.duration 0.08 0.07 -15.2%
s100.pipeline.stage.duration 38.94 38.56 -1.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.pipeline.stage.instructions.count 14.00 14.00 +0.0% ▫️
s100.portrayal.cache.hit.count 13.00 13.00 +0.0% ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 1.67 1.76 +5.4%
s100.render.instructions.processed.count 14.00 14.00 +0.0% ▫️
s100.render.styles.applied.count 14.00 14.00 +0.0% ▫️
s100.xslt.transform.duration 8.81 8.72 -0.9% ▫️

s201-vector

Iteration statistics

Stat Baseline Candidate
Samples 20 20
Median (ms) 0.27 0.28
Baseline MAD (ms) 0.02
Δ median +1.5%
z (Δ/MAD) +0.18

Spans (sum of all iterations)

Span Baseline (ms) Candidate (ms) Delta Status
s100.pipeline.vector.process 5.92 6.01 +1.4% ▫️
s100.pipeline.vector.stage.assemble 0.10 0.09 -8.3% ▫️
s100.pipeline.vector.stage.feature_xml 0.77 0.83 +8.6%
s100.pipeline.vector.stage.rule_select 0.25 0.24 -1.4% ▫️
s100.pipeline.vector.stage.sort 0.11 0.11 +3.5% ▫️
s100.pipeline.vector.stage.viewing_groups 0.39 0.38 -0.6% ▫️
s100.pipeline.vector.stage.xslt 2.90 2.85 -1.9% ▫️
s100.render.frame 0.30 0.31 +4.2% ▫️
s100.xslt.transform 1.70 1.69 -0.2% ▫️

Metrics

Metric Baseline Candidate Delta Status
s100.catalogue.match.count 1.00 1.00 +0.0% ▫️
s100.featurecatalogue.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.pipeline.drawinginstructions.out 0.00 0.00 N/A ▫️
s100.pipeline.duration 116.72 113.10 -3.1% ▫️
s100.pipeline.features.in 7.00 7.00 +0.0% ▫️
s100.pipeline.stage.duration 0.01 0.01 -13.8%
s100.pipeline.stage.duration 5.67 5.68 +0.1% ▫️
s100.pipeline.stage.duration 0.11 0.09 -12.4%
s100.pipeline.stage.duration 0.05 0.05 -8.4% ▫️
s100.pipeline.stage.duration 0.04 0.04 -3.9% ▫️
s100.pipeline.stage.duration 110.44 106.85 -3.3% ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.pipeline.stage.instructions.count 0.00 0.00 N/A ▫️
s100.portrayal.cache.hit.count 7.00 7.00 +0.0% ▫️
s100.portrayal.cache.hit.count 6.00 6.00 +0.0% ▫️
s100.portrayal.cache.miss.count 1.00 1.00 +0.0% ▫️
s100.render.frame.duration 0.06 0.06 -6.4% ▫️
s100.render.instructions.processed.count 0.00 0.00 N/A ▫️
s100.render.styles.applied.count 0.00 0.00 N/A ▫️
s100.xslt.transform.duration 18.09 18.08 -0.1% ▫️

Generated by EncDotNet.S100.PerfReport gate command

Phillip Hoff and others added 2 commits May 21, 2026 23:19
ReorderDatasetLayers previously only reordered layers in the
input list and silently ignored layers it had never seen. Two
consequences:

1. Toggling a dataset's Active flag off filtered it out of the
   reorder list, but its layer remained in map.Layers and kept
   painting.
2. R-101-102-B builds a *new* filtered MemoryLayer to replace the
   S-101 area layer when S-102 is active. That replica was not in
   _datasetLayers, so the reorder skipped it entirely — meaning
   the DepArea/DepCnt suppression has been an invisible no-op
   since PR-L2.

Treat the supplied list as the authoritative dataset-layer slice:
remove every previously-tracked dataset layer from map.Layers,
then insert each input layer in order and track it as a dataset
layer. Rule-filtered replicas now actually replace their
originals, and inactive datasets disappear from the map.

Updated IMapHost XML doc to document the new contract.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DockPanel.LastChildFill only stretches the last child, so when the
empty-state Border and populated ScrollViewer were both non-dock
siblings only the ScrollViewer filled. The empty Border took its
content size at the top of the panel instead of centring.

Wrap both in a single Grid that takes the fill slot so the empty
state can centre horizontally and vertically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@philliphoff philliphoff merged commit 9d23732 into main May 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant